From dd339699cd9bd6aae0279afeeb70557bfb6370a7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 30 Nov 2016 09:21:37 -0500 Subject: [PATCH 001/487] Oops, I forgot to remove the uiImage code from the GTK+ port when splitting uiTable into a branch. Fixed the build for now. Fixes #238. --- unix/uipriv_unix.h | 1 + 1 file changed, 1 insertion(+) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index d52a83c2..33ff1e35 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -53,6 +53,7 @@ extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); extern ptrdiff_t *graphemes(const char *text, PangoContext *context); // image.c +/*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); // cellrendererbutton.c From cb927659faa8f826af490bbfa7ac24d48f9d6bad Mon Sep 17 00:00:00 2001 From: Lailton Fernando Mariano Date: Thu, 1 Dec 2016 14:32:14 -0200 Subject: [PATCH 002/487] Update uipriv_darwin.h --- darwin/uipriv_darwin.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 78f31af5..ad1627d0 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -17,6 +17,8 @@ #define NSAppKitVersionNumber10_9 1265 #endif +typedef struct uiImage uiImage; + // menu.m @interface menuManager : NSObject { struct mapTable *items; From 39d1c0565eb58d38716960fb4149180ee2ce5746 Mon Sep 17 00:00:00 2001 From: Lailton Fernando Mariano Date: Thu, 1 Dec 2016 16:26:59 -0200 Subject: [PATCH 003/487] Update uipriv_darwin.h --- darwin/uipriv_darwin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index ad1627d0..6bca87b2 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -17,7 +17,7 @@ #define NSAppKitVersionNumber10_9 1265 #endif -typedef struct uiImage uiImage; +/*TODO remove this*/typedef struct uiImage uiImage; // menu.m @interface menuManager : NSObject { From c0f91058c44e7c2ed449188ae6fda795b824933c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 11:20:22 -0500 Subject: [PATCH 004/487] Started the move to my utf library. Imported the library. --- common/utf.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++ common/utf.h | 61 +++++++++ 2 files changed, 408 insertions(+) create mode 100644 common/utf.c create mode 100644 common/utf.h diff --git a/common/utf.c b/common/utf.c new file mode 100644 index 00000000..9efb9493 --- /dev/null +++ b/common/utf.c @@ -0,0 +1,347 @@ +// utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ +// 10 november 2016 +#include "utf.h" + +// this code imitates Go's unicode/utf8 and unicode/utf16 +// the biggest difference is that a rune is unsigned instead of signed (because Go guarantees what a right shift on a signed number will do, whereas C does not) +// it is also an imitation so we can license it under looser terms than the Go source +#define badrune 0xFFFD + +// encoded must be at most 4 bytes +// TODO clean this code up somehow +size_t utf8EncodeRune(uint32_t rune, char *encoded) +{ + uint8_t b, c, d, e; + size_t n; + + // not in the valid range for Unicode + if (rune > 0x10FFFF) + rune = badrune; + // surrogate runes cannot be encoded + if (rune >= 0xD800 && rune < 0xE000) + rune = badrune; + + if (rune < 0x80) { // ASCII bytes represent themselves + b = (uint8_t) (rune & 0xFF); + n = 1; + goto done; + } + if (rune < 0x800) { // two-byte encoding + c = (uint8_t) (rune & 0x3F); + c |= 0x80; + rune >>= 6; + b = (uint8_t) (rune & 0x1F); + b |= 0xC0; + n = 2; + goto done; + } + if (rune < 0x10000) { // three-byte encoding + d = (uint8_t) (rune & 0x3F); + d |= 0x80; + rune >>= 6; + c = (uint8_t) (rune & 0x3F); + c |= 0x80; + rune >>= 6; + b = (uint8_t) (rune & 0x0F); + b |= 0xE0; + n = 3; + goto done; + } + // otherwise use a four-byte encoding + e = (uint8_t) (rune & 0x3F); + e |= 0x80; + rune >>= 6; + d = (uint8_t) (rune & 0x3F); + d |= 0x80; + rune >>= 6; + c = (uint8_t) (rune & 0x3F); + c |= 0x80; + rune >>= 6; + b = (uint8_t) (rune & 0x07); + b |= 0xF0; + n = 4; + +done: + encoded[0] = b; + if (n > 1) + encoded[1] = c; + if (n > 2) + encoded[2] = d; + if (n > 3) + encoded[3] = e; + return n; +} + +const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune) +{ + uint8_t b, c; + uint8_t lowestAllowed, highestAllowed; + size_t i, expected; + int bad; + + b = (uint8_t) (*s); + if (b < 0x80) { // ASCII bytes represent themselves + *rune = b; + s++; + return s; + } + // 0xC0 and 0xC1 cover 2-byte overlong equivalents + // 0xF5 to 0xFD cover values > 0x10FFFF + // 0xFE and 0xFF were never defined (always illegal) + if (b < 0xC2 || b > 0xF4) { // invalid + *rune = badrune; + s++; + return s; + } + + // this determines the range of allowed first continuation bytes + lowestAllowed = 0x80; + highestAllowed = 0xBF; + switch (b) { + case 0xE0: + // disallow 3-byte overlong equivalents + lowestAllowed = 0xA0; + break; + case 0xED: + // disallow surrogate characters + highestAllowed = 0x9F; + break; + case 0xF0: + // disallow 4-byte overlong equivalents + lowestAllowed = 0x90; + break; + case 0xF4: + // disallow values > 0x10FFFF + highestAllowed = 0x8F; + break; + } + + // and this determines how many continuation bytes are expected + expected = 1; + if (b >= 0xE0) + expected++; + if (b >= 0xF0) + expected++; + if (nElem != 0) { // are there enough bytes? + nElem--; + if (nElem < expected) { // nope + *rune = badrune; + s++; + return s; + } + } + + // ensure that everything is correct + // if not, **only** consume the initial byte + bad = 0; + for (i = 0; i < expected; i++) { + c = (uint8_t) (s[1 + i]); + if (c < lowestAllowed || c > highestAllowed) { + bad = 1; + break; + } + // the old lowestAllowed and highestAllowed is only for the first continuation byte + lowestAllowed = 0x80; + highestAllowed = 0xBF; + } + if (bad) { + *rune = badrune; + s++; + return s; + } + + // now do the topmost bits + if (b < 0xE0) + *rune = b & 0x1F; + else if (b < 0xF0) + *rune = b & 0x0F; + else + *rune = b & 0x07; + s++; // we can finally move on + + // now do the continuation bytes + for (; expected; expected--) { + c = (uint8_t) (*s); + s++; + c &= 0x3F; // strip continuation bits + *rune <<= 6; + *rune |= c; + } + + return s; +} + +// encoded must have at most 2 elements +size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded) +{ + uint16_t low, high; + + // not in the valid range for Unicode + if (rune > 0x10FFFF) + rune = badrune; + // surrogate runes cannot be encoded + if (rune >= 0xD800 && rune < 0xE000) + rune = badrune; + + if (rune < 0x10000) { + encoded[0] = (uint16_t) rune; + return 1; + } + + rune -= 0x10000; + low = (uint16_t) (rune & 0x3FF); + rune >>= 10; + high = (uint16_t) (rune & 0x3FF); + encoded[0] = high | 0xD800; + encoded[1] = low | 0xDC00; + return 2; +} + +// TODO see if this can be cleaned up somehow +const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) +{ + uint16_t high, low; + + if (*s < 0xD800 || *s >= 0xE000) { + // self-representing character + *rune = *s; + s++; + return s; + } + if (*s >= 0xDC00) { + // out-of-order surrogates + *rune = badrune; + s++; + return s; + } + if (nElem == 1) { // not enough elements + *rune = badrune; + s++; + return s; + } + high = *s; + high &= 0x3FF; + if (s[1] < 0xDC00 || s[1] >= 0xE000) { + // bad surrogate pair + *rune = badrune; + s++; + return s; + } + s++; + low = *s; + s++; + low &= 0x3FF; + *rune = high; + *rune <<= 10; + *rune |= low; + *rune += 0x10000; + return s; +} + +// TODO find a way to reduce the code in all of these somehow +// TODO find a way to remove u as well +size_t utf8RuneCount(const char *s, size_t nElem) +{ + size_t len; + uint32_t rune; + + if (nElem != 0) { + const char *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf8DecodeRune(t, nElem, &rune); + len++; + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf8DecodeRune(s, nElem, &rune); + len++; + } + return len; +} + +size_t utf8UTF16Count(const char *s, size_t nElem) +{ + size_t len; + uint32_t rune; + uint16_t encoded[2]; + + if (nElem != 0) { + const char *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf8DecodeRune(t, nElem, &rune); + len += utf16EncodeRune(rune, encoded); + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf8DecodeRune(s, nElem, &rune); + len += utf16EncodeRune(rune, encoded); + } + return len; +} + +size_t utf16RuneCount(const uint16_t *s, size_t nElem) +{ + size_t len; + uint32_t rune; + + if (nElem != 0) { + const uint16_t *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf16DecodeRune(t, nElem, &rune); + len++; + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf16DecodeRune(s, nElem, &rune); + len++; + } + return len; +} + +size_t utf16UTF8Count(const uint16_t *s, size_t nElem) +{ + size_t len; + uint32_t rune; + char encoded[4]; + + if (nElem != 0) { + const uint16_t *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf16DecodeRune(t, nElem, &rune); + len += utf8EncodeRune(rune, encoded); + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf16DecodeRune(s, nElem, &rune); + len += utf8EncodeRune(rune, encoded); + } + return len; +} diff --git a/common/utf.h b/common/utf.h new file mode 100644 index 00000000..b810a49d --- /dev/null +++ b/common/utf.h @@ -0,0 +1,61 @@ +// utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ +// 10 november 2016 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// if nElem == 0, assume the buffer has no upper limit and is '\0' terminated +// otherwise, assume buffer is NOT '\0' terminated but is bounded by nElem *elements* + +extern size_t utf8EncodeRune(uint32_t rune, char *encoded); +extern const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune); +extern size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded); +extern const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune); + +extern size_t utf8RuneCount(const char *s, size_t nElem); +extern size_t utf8UTF16Count(const char *s, size_t nElem); +extern size_t utf16RuneCount(const uint16_t *s, size_t nElem); +extern size_t utf16UTF8Count(const uint16_t *s, size_t nElem); + +#ifdef __cplusplus +} + +// Provide overloads on Windows for using these functions with wchar_t and WCHAR when wchar_t is a keyword in C++ mode (the default). +// Otherwise, you'd need to cast to pass a wchar_t pointer, WCHAR pointer, or equivalent to these functions. +// We use __wchar_t to be independent of the setting; see https://blogs.msdn.microsoft.com/oldnewthing/20161201-00/?p=94836 (ironically posted one day after I initially wrote this code!). +// TODO check this on MinGW-w64 +// TODO check this under /Wall +// TODO C-style casts enough? or will that fail in /Wall? +// TODO same for UniChar/unichar on Mac? if both are unsigned then we have nothing to worry about +#if defined(_MSC_VER) + +inline size_t utf16EncodeRune(uint32_t rune, __wchar_t *encoded) +{ + return utf16EncodeRune(rune, reinterpret_cast(encoded)); +} + +inline const __wchar_t *utf16DecodeRune(const __wchar_t *s, size_t nElem, uint32_t *rune) +{ + const uint16_t *ret; + + ret = utf16DecodeRune(reinterpret_cast(s), nElem, rune); + return reinterpret_cast(ret); +} + +inline size_t utf16RuneCount(const __wchar_t *s, size_t nElem) +{ + return utf16RuneCount(reinterpret_cast(s), nElem); +} + +inline size_t utf16UTF8Count(const __wchar_t *s, size_t nElem) +{ + return utf16UTF8Count(reinterpret_cast(s), nElem); +} + +#endif + +#endif From 0d5ff432b379c4ad2d7f59d3fd3d3306d9f35f04 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 11:31:11 -0500 Subject: [PATCH 005/487] Rewrote utf16.cpp to use my utf lib. Maybe I should be doing attributed strings safter all, but I might as well optimize too I guess?? --- common/CMakeLists.txt | 1 + common/uipriv.h | 3 +++ windows/utf16.cpp | 48 ++++++++++++++++++++----------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 91d79493..a4008fd1 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND _LIBUI_SOURCES common/matrix.c common/shouldquit.c common/userbugs.c + common/utf.c ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) diff --git a/common/uipriv.h b/common/uipriv.h index d6b54e89..f22a08a6 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,4 +1,7 @@ // 6 april 2015 +// TODO can extern "C"s nest? +#include "utf.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 98954d0a..6271fff7 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -3,48 +3,42 @@ // see http://stackoverflow.com/a/29556509/3408572 -#define MBTWC(str, wstr, bufsiz) MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, bufsiz) - WCHAR *toUTF16(const char *str) { WCHAR *wstr; - int n; + WCHAR *wp; + size_t n; + uint32_t rune; if (*str == '\0') // empty string return emptyUTF16(); - n = MBTWC(str, NULL, 0); - if (n == 0) { - logLastError(L"error figuring out number of characters to convert to"); - return emptyUTF16(); - } - wstr = (WCHAR *) uiAlloc(n * sizeof (WCHAR), "WCHAR[]"); - if (MBTWC(str, wstr, n) != n) { - logLastError(L"error converting from UTF-8 to UTF-16"); - // and return an empty string - *wstr = L'\0'; + n = utf8UTF16Count(str, 0); + wstr = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); + wp = wstr; + while (*str) { + str = utf8DecodeRune(str, 0, &rune); + n = utf16EncodeRune(rune, wp); + wp += n; } return wstr; } -#define WCTMB(wstr, str, bufsiz) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, bufsiz, NULL, NULL) - char *toUTF8(const WCHAR *wstr) { char *str; - int n; + char *sp; + size_t n; + uint32_t rune; if (*wstr == L'\0') // empty string return emptyUTF8(); - n = WCTMB(wstr, NULL, 0); - if (n == 0) { - logLastError(L"error figuring out number of characters to convert to"); - return emptyUTF8(); - } - str = (char *) uiAlloc(n * sizeof (char), "char[]"); - if (WCTMB(wstr, str, n) != n) { - logLastError(L"error converting from UTF-16 to UTF-8"); - // and return an empty string - *str = '\0'; + n = utf16RuneCount(wstr, 0); + str = (char *) uiAlloc((n + 1) * sizeof (char), "char[]"); + sp = str; + while (*wstr) { + wstr = utf16DecodeRune(wstr, &rune); + n = utf8EncodeRune(rune, sp); + sp += n; } return str; } @@ -92,6 +86,8 @@ WCHAR *vstrf(const WCHAR *format, va_list ap) return buf; } +// TODO merge the following two with the toUTF*()s? + // Let's shove these utility routines here too. // Prerequisite: lfonly is UTF-8. char *LFtoCRLF(const char *lfonly) From 526173bf76bb064084fa3f89e249bb40bf8c487b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 18:34:06 -0500 Subject: [PATCH 006/487] Started writing the implementation of uiAttributedString. Updated windows/graphemes.cpp for this new implementation. Also fixed up a bunch of subtle errors and other issues with windows/graphemes.cpp. --- common/attrstr.c | 160 ++++++++++++++++++++++++++++++++++++++++++ common/uipriv.h | 12 +++- windows/graphemes.cpp | 80 ++++++++++++++------- 3 files changed, 223 insertions(+), 29 deletions(-) create mode 100644 common/attrstr.c diff --git a/common/attrstr.c b/common/attrstr.c new file mode 100644 index 00000000..03a1dadc --- /dev/null +++ b/common/attrstr.c @@ -0,0 +1,160 @@ +// 3 december 2016 +#include "../ui.h" +#include "uipriv.h" + +struct uiAttributedString { + char *s; + size_t len; + + // TODO attributes + + // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator + // this ensures no one platform has a speed advantage (sorry GTK+) + uint16_t *u16; + size_t u16len; + + size_t *u8tou16; + size_t *u16tou8; + + // this is lazily created to keep things from getting *too* slow + struct graphemes *graphemes; +}; + +static void resize(uiAttributedString *s, size_t u8, size_t u16) +{ + s->len = u8; + s->s = (char *) uiRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)"); + s->u8tou16 = (size_t *) uiRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); + s->u16len = u16; + s->u16 = (uint16_t *) uiRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)"); + s->u16tou8 = (size_t *) uiRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); +} + +uiAttributedString *uiNewAttributedString(const char *initialString) +{ + uiAttributedString *s; + + s = uiNew(uiAttributedString); + uiAttributedStringAppendUnattributed(s, initialString); + return s; +} + +static void recomputeGraphemes(uiAttributedString *s) +{ + if (s->graphemes != NULL) + return; + if (graphemesTakesUTF16()) { + s->graphemes = graphemes(s->u16, s->u16len); + return; + } + s->graphemes = graphemes(s->s, s->len); +} + +static void invalidateGraphemes(uiAttributedString *s) +{ + if (s->graphemes == NULL) + return; + uiFree(s->graphemes->pointsToGraphemes); + uiFree(s->graphemes->graphemesToPoints); + uiFree(s->graphemes); + s->graphemes = NULL; +} + +void uiFreeAttributedString(uiAttributedString *s) +{ + invalidateGraphemes(s); + uiFree(s->u16tou8); + uiFree(s->u8tou16); + uiFree(s->u16); + uiFree(s->s); + uiFree(s); +} + +const char *uiAttributedStringString(uiAttributedString *s) +{ + return s->s; +} + +void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str) +{ + const char *t; + uint32_t rune; + char buf[4]; + uint16_t u16buf[2]; + size_t n, n16; + size_t old, old; + + // first figure out how much we need to grow by + // this includes post-validated UTF-8 + t = str; + n = 0; + n16 = 0; + while (*t) { + t = utf8DecodeRune(t, 0, &rune); + n += utf8EncodeRune(rune, buf); + n16 += utf16EncodeRune(rune, buf16); + } + + // and resize + old = s->len; + old16 = s->len16; + resize(s, s->len + n, s->u16len + n16); + + // and copy + while (*str) { + str = utf8DecodeRune(str, 0, &rune); + n = utf8EncodeRune(rune, buf); + n16 = utf16EncodeRune(rune, buf16); + s->s[old] = buf[0]; + s->u8tou16[old] = old16; + if (n > 1) { + s->s[old + 1] = buf[1]; + s->u8tou16[old + 1] = old16; + } + if (n > 2) { + s->s[old + 2] = buf[2]; + s->u8tou16[old + 2] = old16; + } + if (n > 3) { + s->s[old + 3] = buf[3]; + s->u8tou16[old + 3] = old16; + } + s->u16[old16] = buf16[0]; + s->u16tou8[old16] = old; + if (n16 > 1) { + s->u16[old16 + 1] = buf16[1]; + s->u16tou8[old16 + 1] = old; + } + old += n; + old16 += n16; + } + // and have an index for the end of the string + s->u8tou16[old] = old16; + s->u16tou8[old16] = old; + + invalidateGraphemes(s); +} + +// TODO figure out if we should count the grapheme past the end +size_t uiAttributedStringNumGraphemes(uiAttributedString *s) +{ + recomputeGraphemes(s); + return s->graphemes->len; +} + +size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos) +{ + recomputeGraphemes(s); + if (graphemesTakesUTF16()) + pos = s->u8tou16[pos]; + return s->graphemes->pointsToGraphemes[pos]; +} + +size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) +{ + recomputeGraphemes(s); + pos = s->graphemes->graphemesToPoints[pos]; + if (graphemesTakesUTF16()) + pos = s->u16tou8[pos]; + return pos; +} diff --git a/common/uipriv.h b/common/uipriv.h index f22a08a6..1fe00b16 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,6 +1,4 @@ // 6 april 2015 -// TODO can extern "C"s nest? -#include "utf.h" #ifdef __cplusplus extern "C" { @@ -8,6 +6,7 @@ extern "C" { #include #include "controlsigs.h" +#include "utf.h" extern uiInitOptions options; @@ -56,6 +55,15 @@ extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); +// for attrstr.c +struct graphemes { + size_t len; + size_t *pointsToGraphemes; + size_t *graphemesToPoints; +}; +extern int graphemesTakesUTF16(void); +extern struct graphemes *graphemes(void *s, size_t len); + #ifdef __cplusplus } #endif diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 355e4037..d4fc1e1b 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -3,9 +3,14 @@ // We could use CharNext() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). // So let's use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html) -// See also http://www.catch22.net/tuts/uniscribe-mysteries and http://www.catch22.net/tuts/keyboard-navigation for more details. +// See also http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/ for more details. -static HRESULT itemize(WCHAR *msg, size_t len, SCRIPT_ITEM **out, int *outn) +int graphemesTakesUTF16(void) +{ + return 1; +} + +static HRESULT itemize(WCHAR *s, size_t len, SCRIPT_ITEM **out, int *outn) { SCRIPT_CONTROL sc; SCRIPT_STATE ss; @@ -20,8 +25,8 @@ static HRESULT itemize(WCHAR *msg, size_t len, SCRIPT_ITEM **out, int *outn) maxItems = len + 2; for (;;) { - items = new SCRIPT_ITEM[maxItems]; - hr = ScriptItemize(msg, len, + items = new SCRIPT_ITEM[maxItems + 1]; + hr = ScriptItemize(s, len, maxItems, &sc, &ss, items, &n); @@ -39,42 +44,63 @@ static HRESULT itemize(WCHAR *msg, size_t len, SCRIPT_ITEM **out, int *outn) return S_OK; } -size_t *graphemes(WCHAR *msg) +struct graphemes *graphemes(void *s, size_t len) { - size_t len; + struct graphemes *g; + WCHAR *str = (WCHAR *) s; SCRIPT_ITEM *items; - int i, n; - size_t *out; - size_t *op; - SCRIPT_LOGATTR *logattr; - int j, nn; + int nItems; + int curItemIndex; + int nCharsInCurItem; + size_t *pPTG, *pGTP; HRESULT hr; - len = wcslen(msg); - hr = itemize(msg, len, &items, &n); + g = uiNew(struct graphemes); + + hr = itemize(str, len, &items, &n); if (hr != S_OK) logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); + g->len = nItems; + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + // note that there are actually nItems + 1 elements in items + // items[nItems] is the grapheme one past the end + g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - // should be enough; 2 more just to be safe - out = (size_t *) uiAlloc((len + 2) * sizeof (size_t), "size_t[]"); - op = out; + pPTG = g->pointsToGraphemes; + pGTP = g->graphemesToPoints; + for (curItemIndex = 0; curItemIndex < nItems; curItemIndex++) { + SCRIPT_ITEM *curItem, *nextItem; + SCRIPT_LOGATTR *logattr; + size_t *curGTP; - // note that there are actually n + 1 elements in items - for (i = 0; i < n; i++) { - nn = items[i + 1].iCharPos - items[i].iCharPos; - logattr = new SCRIPT_LOGATTR[nn]; - hr = ScriptBreak(msg + items[i].iCharPos, nn, - &(items[i].a), logattr); + curItem = items + curItemIndex; + nextItem = curItem + 1; + + nCharsInCurItem = nextItem->iCharPos - curItem->iCharPos; + + logattr = new SCRIPT_LOGATTR[nCharsInCurItem]; + hr = ScriptBreak(str + curItem->iCharPos, nCharsInCurItem, + &(curItem->a), logattr); if (hr != S_OK) logHRESULT(L"error breaking string for finding grapheme cluster boundaries", hr); - for (j = 0; j < nn; j++) - if (logattr[j].fCharStop != 0) - *op++ = items[i].iCharPos + j; + + // TODO can we merge these loops somehow? + curGTP = pGTP; + for (i = 0; i < nCharsInCurItem; i++) + if (logattr[i].fCharStop != 0) + *pGTP++ = curItem->iCharPos + i; + for (i = 0; i < nCharsInCurItem; i++) { + *pPTG++ = curGTP - g->graphemesToPoints; + if (logattr[i].fCharStop != 0) + curGTP++; + } + delete[] logattr; } // and handle the last item for the end of the string - *op++ = items[i].iCharPos; + *pGTP++ = items[nItems].iCharPos; + *pPTG++ = pGTP - g->graphemesToPoints; delete[] items; - return out; + return g; } From 3218ba2a439120ca1cfa674290ba8b21e4246f2a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 18:53:46 -0500 Subject: [PATCH 007/487] And migrated the Unix grapheme code for the new attributed string system. --- unix/graphemes.c | 55 +++++++++++++++++++++++++++++--------- unix/uipriv_unix.h | 3 --- windows/uipriv_windows.hpp | 3 --- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/unix/graphemes.c b/unix/graphemes.c index a2c47b72..e489ebff 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -1,24 +1,41 @@ // 25 may 2016 #include "uipriv_unix.h" -ptrdiff_t *graphemes(const char *text, PangoContext *context) +int graphemesTakesUTF16(void) { - size_t len, lenchars; - PangoLogAttr *logattrs; - ptrdiff_t *out; - ptrdiff_t *op; - size_t i; + return 0; +} - len = strlen(text); +struct graphemes *graphemes(void *s, size_t len); +{ + struct graphemes *g; + char *text = (char *) s; + size_t lenchars; + PangoLogAttr *logattrs; + size_t i; + size_t *op; + + g = uiNew(struct graphemes); + + // TODO see if we can use the utf routines lenchars = g_utf8_strlen(text, -1); - logattrs = (PangoLogAttr *) uiAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[]"); + logattrs = (PangoLogAttr *) uiAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[] (graphemes)"); pango_get_log_attrs(text, len, -1, NULL, logattrs, lenchars + 1); - // should be more than enough - out = (ptrdiff_t *) uiAlloc((lenchars + 2) * sizeof (ptrdiff_t), "ptrdiff_t[]"); - op = out; + // first figure out how many graphemes there are + g->len = 0; + for (i = 0; i < lenchars; i++) + if (logattrs[i].is_cursor_position != 0) + g->len++; + + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + + // compute the graphemesToPoints array + // TODO merge with the next for loop somehow? + op = g->graphemesToPoints; for (i = 0; i < lenchars; i++) if (logattrs[i].is_cursor_position != 0) // TODO optimize this @@ -26,6 +43,20 @@ ptrdiff_t *graphemes(const char *text, PangoContext *context) // and do the last one *op++ = len; + // and finally build the pointsToGraphemes array + op = g->pointsToGraphemes; + for (i = 0; i < g->len; i++) { + size_t j; + size_t first, last; + + first = g->graphemesToPoints[i]; + last = g->graphemesToPoints[i + 1]; + for (j = first; j < last; j++) + *op++ = i; + } + // and do the last one + *op++ = i; + uiFree(logattrs); - return out; + return g; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 33ff1e35..de2c1d3d 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -49,9 +49,6 @@ extern void freeContext(uiDrawContext *); extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); -// graphemes.c -extern ptrdiff_t *graphemes(const char *text, PangoContext *context); - // image.c /*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 6ffe09f1..2354bfd7 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -148,9 +148,6 @@ extern BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c); // sizing.cpp extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); -// graphemes.cpp -extern size_t *graphemes(WCHAR *msg); - // TODO move into a dedicated file abibugs.cpp when we rewrite the drawing code extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); From da8b8de37141f2774d2b2f4e84ff3340493b90fb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 20:01:45 -0500 Subject: [PATCH 008/487] Wrote the new attributed string system's grapheme code for OS X. --- darwin/graphemes.m | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 darwin/graphemes.m diff --git a/darwin/graphemes.m b/darwin/graphemes.m new file mode 100644 index 00000000..3819f0b4 --- /dev/null +++ b/darwin/graphemes.m @@ -0,0 +1,58 @@ +// 3 december 2016 +#import "uipriv_darwin.h" + +// CFStringGetRangeOfComposedCharactersAtIndex() is the function for grapheme clusters +// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway + +int graphemesTakesUTF16(void) +{ + return 1; +} + +struct graphemes *graphemes(void *s, size_t len) +{ + struct graphemes *g; + UniChar *str = (UniChar *) s; + CFString cfstr; + size_t ppos, gpos; + CFRange range; + size_t i; + + g = uiNew(struct graphemes); + + cfstr = CFStringCreateWithCharactersNoCopy(NULL, str, len, kCFAllocatorNull); + if (cfstr == NULL) { + // TODO + } + + // first figure out how many graphemes there are + g->len = 0; + ppos = 0; + while (ppos < len) { + range = CFStringGetRangeOfComposedCharactersAtIndex(cfstr, ppos); + g->len++; + ppos = range.location + range.length; + } + + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + + // now calculate everything + // fortunately due to the use of CFRange we can do this in one loop trivially! + ppos = 0; + gpos = 0; + while (ppos < len) { + range = CFStringGetRangeOfComposedCharactersAtIndex(cfstr, ppos); + for (i = 0; i < range.length; i++) + g->pointsToGraphemes[range.location + i] = gpos; + g->graphemesToPoints[gpos] = range.location; + gpos++; + ppos = range.location + range.length; + } + // and set the last one + g->pointsToGraphemes[ppos] = gpos; + g->graphemesToPoints[gpos] = ppos; + + CFRelease(cfstr); + return g; +} From 4f8f94b85ab81c5111a442abddb75851a242362a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 20:02:39 -0500 Subject: [PATCH 009/487] Updated the CMakeLists.txt for OS X. --- darwin/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index dbef5d43..dc99b864 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -19,6 +19,7 @@ list(APPEND _LIBUI_SOURCES darwin/entry.m darwin/fontbutton.m darwin/form.m + darwin/graphemes.m darwin/grid.m darwin/group.m darwin/image.m From b45e5f4de2fba1d0ed189529095bcd275da94738 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Dec 2016 16:02:56 -0500 Subject: [PATCH 010/487] Extended attrstr.c. Much more complete API now. --- common/attrstr.c | 150 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 138 insertions(+), 12 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 03a1dadc..98e8e90a 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -39,6 +39,7 @@ uiAttributedString *uiNewAttributedString(const char *initialString) return s; } +// TODO make sure that all implementations of graphemes() work fine with empty strings; in particular, the Windows one might not static void recomputeGraphemes(uiAttributedString *s) { if (s->graphemes != NULL) @@ -75,31 +76,95 @@ const char *uiAttributedStringString(uiAttributedString *s) return s->s; } +size_t uiAttributedStringLen(uiAttributedString *s) +{ + return s->len; +} + +static void u8u16len(const char *str, size_t *n8, size_t *n16) +{ + uint32_t rune; + char buf[4]; + uint16_t buf16[2]; + + *n8 = 0; + *n16 = 0; + while (*str) { + str = utf8DecodeRune(str, 0, &rune); + *n8 += utf8EncodeRune(rune, buf); + *n16 += utf16EncodeRune(rune, buf16); + } +} + void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str) { - const char *t; + uiAttributedStringInsertAtUnattributed(s, str, s->len); +} + +// this works (and returns true, which is what we want) at s->len too because s->s[s->len] is always going to be 0 due to us allocating s->len + 1 bytes and because uiRealloc() always zero-fills allocated memory +static int onCodepointBoundary(uiAttributedString *s, size_t at) +{ + uint8_t c; + + // for uiNewAttributedString() + if (s->s == NULL && at == 0) + return 1; + c = (uint8_t) (s->s[at]); + return c < 0x80 || c >= 0xC0; +} + +// TODO note that at must be on a codeoint boundary +void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at) +{ uint32_t rune; char buf[4]; uint16_t u16buf[2]; size_t n, n16; - size_t old, old; + size_t old, old16; + size_t oldlen, old16len; + size_t at16; + size_t i; + + if (!onCodepointBoundary(s, at)) { + // TODO + } + + at16 = s->u8tou16[at]; + + // do this first to reclaim memory + invalidateGraphemes(s); // first figure out how much we need to grow by // this includes post-validated UTF-8 - t = str; - n = 0; - n16 = 0; - while (*t) { - t = utf8DecodeRune(t, 0, &rune); - n += utf8EncodeRune(rune, buf); - n16 += utf16EncodeRune(rune, buf16); - } + u8u16len(str, &n, &n16); // and resize - old = s->len; - old16 = s->len16; + old = at; + old16 = at16; + oldlen = s->len; + old16len = s->u16len; resize(s, s->len + n, s->u16len + n16); + // move existing characters out of the way + // note the use of memmove(): https://twitter.com/rob_pike/status/737797688217894912 + memmove( + s->s + at + n8, + s->s + at, + (oldlen - at) * sizeof (char)); + memmove( + s->u16 + at16 + n16, + s->u16 + at16, + (old16len - at16) * sizeof (uint16_t)); + // note the + 1 for these; we want to copy the terminating null too + memmove( + s->u8tou16 + at + n8, + s->u8tou16 + at, + (oldlen - at + 1) * sizeof (size_t)); + memmove( + s->u16tou8 + at16 + n16, + s->u16tou8 + at16, + (old16len - at16 + 1) * sizeof (size_t)); + // and copy while (*str) { str = utf8DecodeRune(str, 0, &rune); @@ -129,10 +194,71 @@ void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str old16 += n16; } // and have an index for the end of the string + // TODO is this done by the below? s->u8tou16[old] = old16; s->u16tou8[old16] = old; + // and finally adjust the prior values in the conversion tables + // use <= so the terminating 0 gets updated too + for (i = 0; i <= oldlen - at; i++) + s->u8tou16[at + n + i] += n16; + for (i = 0; i <= old16len - at16; i++) + s->u16tou8[at16 + n16 + i] += n; +} + +// TODO document that end is the first index that will be maintained +void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) +{ + size_t start16, end16; + size_t count, count16; + size_t i; + + if (!onCodepointBoundary(s, start)) { + // TODO + } + if (!onCodepointBoundary(s, end)) { + // TODO + } + + count = end - start; + start16 = s->u8tou16[start]; + end16 = s->u8tou16[end]; + count16 = end16 - start16; + invalidateGraphemes(s); + + // overwrite old characters + memmove( + s->s + start, + s->s + end, + (oldlen - end) * sizeof (char)); + memmove( + s->u16 + start16, + s->u16 + end16, + (old16len - end16) * sizeof (uint16_t)); + // note the + 1 for these; we want to copy the terminating null too + memmove( + s->u8tou16 + start, + s->u8tou16 + end, + (oldlen - end + 1) * sizeof (size_t)); + memmove( + s->u16tou8 + start16, + s->u16tou8 + end16, + (old16len - end16 + 1) * sizeof (size_t)); + + // update the conversion tables + // note the use of <= to include the null terminator + for (i = 0; i <= count; i++) + s->u8tou16[start + i] -= count16; + for (i = 0; i <= count16; i++) + s->u16tou8[start16 + i] -= count; + + // null-terminate the string + s->s[start + count] = 0; + s->u16[start16 + count16] = 0; + + // and finally resize + resize(s, start + count, start16 + count16); } // TODO figure out if we should count the grapheme past the end From cb8d75d43168aea043571b387bda0c3a5ab57af6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Dec 2016 18:32:51 -0500 Subject: [PATCH 011/487] Started implementing the attribute handling code itself. --- common/attrstr.c | 260 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 253 insertions(+), 7 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 98e8e90a..bc28242c 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -6,7 +6,9 @@ struct uiAttributedString { char *s; size_t len; - // TODO attributes + size_t nAttrs; // TODO this needs to be maintained; is it necessary? + struct attr *attrs; + struct attr *lastattr; // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator // this ensures no one platform has a speed advantage (sorry GTK+) @@ -20,6 +22,237 @@ struct uiAttributedString { struct graphemes *graphemes; }; +struct attr { + int type; + uintptr_t val; + size_t start; + size_t end; + struct attr *next; +}; + +enum { + // TODO put attr types here + nAttrTypes, +}; + +static int attrHasPos(struct attr *a, size_t pos) +{ + if (pos < a->start) + return 0; + return pos < a->end; +} + +// returns 1 if there was an intersection and 0 otherwise +static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) +{ + // is the range outside a entirely? + if (*start >= a->end) + return 0; + if (*end < a->start) + return 0; + + // okay, so there is an overlap + // compute the intersection + if (*start < a->start) + *start = a->start; + if (*end > a->end) + *end = a->end; + return 1; +} + +// returns: +// - 0 if no change needed +// - 1 if the attribute was split +static int attrSplitAt(uiAttributedString *s, struct attr *a, size_t at) +{ + struct attr *b; + + // no splittng needed? + if (at == a->start) + return 0; + if ((at + 1) == a->end) + return 0; + + b = uiNew(struct attr); + b->what = a->what; + b->val = a->val; + b->start = at; + b->end = a->end; + b->next = a->next; + + a->end = at; + a->next = b; + if (a == s->lastattr) + s->lastattr = a->next; + return 1; +} + +// returns: +// - 0 if the attribute needs to be deleted +// - 1 if the attribute was changed or no change needed +// - 2 if the attribute was split +static int attrDropRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) +{ + struct attr *b; + + if (!attrRangeIntersect(a, &start, &end)) + // out of range; nothing to do + return 1; + + // just outright delete the attribute? + if (a->start == start && a->end == end) + return 0; + + // only chop off the start or end? + if (a->start == start) { // chop off the end + a->end = end; + return 1; + } + if (a->end == end) { // chop off the start + a->start = start; + return 1; + } + + // we'll need to split the attribute into two + b = uiNew(struct attr); + b->what = a->what; + b->val = a->val; + b->start = end; + b->end = a->end; + b->next = a->next; + + a->end = start; + a->next = b; + if (a == s->lastattr) + s->lastattr = a->next; + return 2; +} + +// returns the attribute to continue with +static struct attr *attrDelete(uiAttributedString *s, struct attr *a, struct attr *prev) +{ + if (a == s->attrs) { + s->attrs = a->next; + uiFree(a); + return s->attrs; + } + if (a == s->lastattr) + s->lastattr = prev; + prev->next = a->next; + uiFree(a); + return prev->next; +} + +static void attrAppend(uiAttributedString *s, int type, uintptr_t val, size_t start, size_t end) +{ + struct attr *a; + + a = uiNew(struct attr); + a->type = type; + a->val = val; + a->start = start; + a->end = end; + if (s->attrs == NULL) { + s->attrs = a; + s->lastattr = a; + return; + } + s->lastattr->next = a; + s->lastattr = a; +} + +// alist should be struct attr *alist[nAttrTypes] +static void attrsGetFor(uiAttributedString *s, struct attr **alist, size_t at) +{ + int i; + struct attr *a; + + // we want the attributes for at + // these are the attributes of at - 1 + // if at == 0. then htese are the attributes at 0 + if (at != 0) + at--; + + // make usre unset attributes are NULL + for (i = 0; i < nAttrTypes; i++) + alist[i] = NULL; + + for (a = s->attrs; a != NULL; a = a->next) { + if (!attrHasPos(a, at)) + continue; + alist[a->type] = a; + } +} + +// TODO have a routine that prunes overridden attributes for a given character from the list? merge it with the above? + +static void attrAdjustPostInsert(uiAttributedString *s, size_t start, size_t n, size_t oldlen) +{ + struct attr *a; + + for (a = s->attrs; a != NULL; a = a->next) { + size_t astart, aend; + int splitNeeded; + + // do we need to adjust this, and where? + astart = start; + aend = oldlen; + if (!attrRangeIntersect(a, &astart, &aend)) + continue; + + // if only part of the attribute falls in the moved area, we need to split at the insertion point and adjust both resultant attributes + // otherwise, we only adjust the original attribute + // split *before* adjusting so that the split is correct + splitNeeded = attrSplit(s, a, astart); + if (a->start >= start) + a->start += n; + if (a->end >= end) + a->end += n; + if (splitNeeded == 1) { + a = a->next; + if (a->start >= start) + a->start += n; + if (a->end >= end) + a->end += n; + } + } +} + +// TODO this is very very wrong +static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end) +{ + struct attr *a, *prev; + + a = s->attrs; + prev = NULL; + while (a != NULL) { + size_t astart, aend; + + // do we need to adjust this, and where? + astart = start; + aend = end; + if (!attrRangeIntersect(a, &astart, &aend)) { + prev = a; + a = a->next; + continue; + } + + switch (attrDropRange(s, a, astart, aend)) { + case 0: // delete + a = attrDelete(s, a, prev); + // keep prev unchanged + break; + case 2: // split + a = a->next; + // fall through + case 1: // existing only needed adjustment + prev = a; + a = a->next; + break; + } + } +} + static void resize(uiAttributedString *s, size_t u8, size_t u16) { s->len = u8; @@ -63,6 +296,14 @@ static void invalidateGraphemes(uiAttributedString *s) void uiFreeAttributedString(uiAttributedString *s) { + struct attr *a, *b; + + a = s->attrs; + while (a != NULL) { + b = a->next; + uiFree(a); + a = b; + } invalidateGraphemes(s); uiFree(s->u16tou8); uiFree(s->u8tou16); @@ -119,7 +360,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s uint32_t rune; char buf[4]; uint16_t u16buf[2]; - size_t n, n16; + size_t n8, n16; size_t old, old16; size_t oldlen, old16len; size_t at16; @@ -136,14 +377,14 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // first figure out how much we need to grow by // this includes post-validated UTF-8 - u8u16len(str, &n, &n16); + u8u16len(str, &n8, &n16); // and resize old = at; old16 = at16; oldlen = s->len; old16len = s->u16len; - resize(s, s->len + n, s->u16len + n16); + resize(s, s->len + n8, s->u16len + n16); // move existing characters out of the way // note the use of memmove(): https://twitter.com/rob_pike/status/737797688217894912 @@ -167,6 +408,8 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // and copy while (*str) { + size_t n; + str = utf8DecodeRune(str, 0, &rune); n = utf8EncodeRune(rune, buf); n16 = utf16EncodeRune(rune, buf16); @@ -198,12 +441,15 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u8tou16[old] = old16; s->u16tou8[old16] = old; - // and finally adjust the prior values in the conversion tables + // and adjust the prior values in the conversion tables // use <= so the terminating 0 gets updated too for (i = 0; i <= oldlen - at; i++) - s->u8tou16[at + n + i] += n16; + s->u8tou16[at + n8 + i] += n16; for (i = 0; i <= old16len - at16; i++) - s->u16tou8[at16 + n16 + i] += n; + s->u16tou8[at16 + n16 + i] += n8; + + // and finally do the attributes + attrAdjustPostInsert(s, at, n8, oldlen); } // TODO document that end is the first index that will be maintained From 0503110ddd5a0a2c629b7adeb389faf72b78fd7e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Dec 2016 10:23:53 -0500 Subject: [PATCH 012/487] Fixed the delete attributes code. --- common/attrstr.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index bc28242c..bcd78052 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -87,6 +87,7 @@ static int attrSplitAt(uiAttributedString *s, struct attr *a, size_t at) return 1; } +// removes attributes without deleting characters // returns: // - 0 if the attribute needs to be deleted // - 1 if the attribute was changed or no change needed @@ -128,6 +129,69 @@ static int attrDropRange(uiAttributedString *s, struct attr *a, size_t start, si return 2; } +// removes attributes while deleting characters +// returns: +// - 0 if the attribute needs to be deleted +// - 1 if the attribute was changed or no change needed +// - 2 if the attribute was split +static int attrDeleteRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) +{ + struct attr *b; + struct attr tmp; + size_t count, acount; + + if (!attrRangeIntersect(a, &start, &end)) + // out of range; nothing to do + return 1; + + // just outright delete the attribute? + if (a->start == start && a->end == end) + return 0; + + acount = a->end - a->start; + count = end - start; + + // only the start or end deleted? + if (a->start == start) { // start deleted + a->end = a->start + (acount - count); + return 1; + } + if (a->end == end) { // end deleted + a->end = a->start + count; + return 1; + } + + // something in the middle deleted + // we ened to split the attribute into *three* + // first, split at the start of he deleted range + tmp.what = a->what; + tmp.val = a->val; + tmp.start = start; + tmp.end = a->end; + tmp.next = a->next; + + a->end = start; + a->next = &tmp; + + // now split at the end + b = uiNew(struct attr); + b->what = a->what; + b->val = a->val; + b->start = end; + b->end = a->end; + b->next = tmp.next; + + tmp.end = end; + tmp.next = b; + + // and now push c back to overwrite the deleted stuff + a->next = b; + b->start -= count; + b->end -= count; + + return 2; +} + // returns the attribute to continue with static struct attr *attrDelete(uiAttributedString *s, struct attr *a, struct attr *prev) { @@ -218,7 +282,6 @@ static void attrAdjustPostInsert(uiAttributedString *s, size_t start, size_t n, } } -// TODO this is very very wrong static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end) { struct attr *a, *prev; @@ -237,7 +300,7 @@ static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end continue; } - switch (attrDropRange(s, a, astart, aend)) { + switch (attrDeleteRange(s, a, astart, aend)) { case 0: // delete a = attrDelete(s, a, prev); // keep prev unchanged From e9fdbf33f3789167e65b9269cf094aaa0255ce99 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Dec 2016 10:35:08 -0500 Subject: [PATCH 013/487] And plugged in the deletion stuff into the rest of attrstr.c. We're on a roll here! --- common/attrstr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index bcd78052..e1d42bed 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -184,11 +184,12 @@ static int attrDeleteRange(uiAttributedString *s, struct attr *a, size_t start, tmp.end = end; tmp.next = b; - // and now push c back to overwrite the deleted stuff + // and now push b back to overwrite the deleted stuff a->next = b; b->start -= count; b->end -= count; - + if (a == s->lastattr) + s->lastattr = a->next; return 2; } @@ -566,6 +567,9 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) s->s[start + count] = 0; s->u16[start16 + count16] = 0; + // fix up attributes + attrAdjustPostDelete(s, start, end); + // and finally resize resize(s, start + count, start16 + count16); } From 3f48bddce0fdda4665ab02f2cb1c395c1dbf7a28 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 12 Dec 2016 06:45:49 -0500 Subject: [PATCH 014/487] Started the header file for attributed strings, drawing text layouts, and the new text system in general. --- common/ui_attrstr.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 common/ui_attrstr.h diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h new file mode 100644 index 00000000..a333fb78 --- /dev/null +++ b/common/ui_attrstr.h @@ -0,0 +1,30 @@ +typedef struct uiAttributedString uiAttributedString; + +_UI_ENUM(uiAttribute) { + // TODO +}; + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(uiAttributedString *s); + +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); From 6726ab70a9fabdb95509c15e24f9f82d43fad934 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 15 Dec 2016 13:39:19 -0500 Subject: [PATCH 015/487] Changed attrstr.c to match ui_attrstr.h. --- common/attrstr.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index e1d42bed..224b6074 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -23,17 +23,15 @@ struct uiAttributedString { }; struct attr { - int type; + uiAttribute type; uintptr_t val; size_t start; size_t end; struct attr *next; }; -enum { - // TODO put attr types here - nAttrTypes, -}; +// if new entries types are added to the end of the uiAttribute enumeration, this MUST be updated! +#define nAttrTypes (TODO + 1) static int attrHasPos(struct attr *a, size_t pos) { @@ -234,7 +232,7 @@ static void attrsGetFor(uiAttributedString *s, struct attr **alist, size_t at) // we want the attributes for at // these are the attributes of at - 1 - // if at == 0. then htese are the attributes at 0 + // if at == 0. then these are the attributes at 0 if (at != 0) at--; From 98082068f69b75a6ef15c52cbf052c20f77f3ae7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 16 Dec 2016 23:31:04 -0500 Subject: [PATCH 016/487] Started an experimental doubly linked list implementation of attribute lists. --- common/exp_attrdll.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 common/exp_attrdll.c diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c new file mode 100644 index 00000000..b4bd5f3c --- /dev/null +++ b/common/exp_attrdll.c @@ -0,0 +1,52 @@ +// 16 december 2016 + +struct attr { + uiAttribute type; + uintptr_t val; + size_t start; + size_t end; + struct attr *prev; + struct attr *next; +}; + +struct attrlist { + struct attr *first; + struct attr *last; +}; + +void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +{ + struct attr *a; + struct attr *after; + + a = uiNew(struct attr); + a->type = type; + a->val = val; + a->start = start; + a->end = end; + + if (alist->first == NULL) { // empty list + alist->first = a; + alist->last = a; + return; + } + + // place a before the first element that starts after a does + for (before = alist->first; before != NULL; before = before->next) + if (before->start > a->start) + break; + if (before == NULL) { // if there is none, add a to the end + alist->last->next = a; + a->prev = alist->last; + alist->last = a; + return; + } + + if (before == alist->first) + alist->first = a; + else // not the first; hook up our new prev + before->prev->next = a; + a->next = before; + a->prev = before->prev; + before->prev = a; +} From 722dd03193a32498aa554f447409385e587a86e9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Dec 2016 12:19:33 -0500 Subject: [PATCH 017/487] Cleaned up exp_attrdll.c a bit. --- common/exp_attrdll.c | 61 +++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index b4bd5f3c..2dfa7440 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -14,6 +14,45 @@ struct attrlist { struct attr *last; }; +// if before is NULL, add to the end of the list +static void linkInsertBefore(struct attrlist *alist, struct attr *what, struct attr *before) +{ + // if the list is empty, this is the first item + if (alist->first == NULL) { + alist->first = a; + alist->last = a; + return; + } + + // add to the end + if (before == NULL) { + struct attr *oldlast; + + oldlast = alist->last; + alist->last = a; + a->prev = oldlast; + oldlast->next = a; + return; + } + + // add to the beginning + if (before == alist->first) { + struct attr *oldfirst; + + oldfirst = alist->first; + alist->first = a; + oldfirst->prev = a; + a->next = oldfirst; + return; + } + + // add to the middle + a->prev = before->prev; + a->next = before; + before->prev = a; + a->prev->next = a; +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -25,28 +64,10 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s a->start = start; a->end = end; - if (alist->first == NULL) { // empty list - alist->first = a; - alist->last = a; - return; - } - // place a before the first element that starts after a does for (before = alist->first; before != NULL; before = before->next) if (before->start > a->start) break; - if (before == NULL) { // if there is none, add a to the end - alist->last->next = a; - a->prev = alist->last; - alist->last = a; - return; - } - - if (before == alist->first) - alist->first = a; - else // not the first; hook up our new prev - before->prev->next = a; - a->next = before; - a->prev = before->prev; - before->prev = a; + // TODO this does not handle cases where the attribute overwrites an existing attribute + linkInsertBefore(alist, a, before); } From 2f449887d880b28379e5df131d3619fca411abb7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Dec 2016 12:23:09 -0500 Subject: [PATCH 018/487] More TODOs. --- common/exp_attrdll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 2dfa7440..45a30a24 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -70,4 +70,5 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s break; // TODO this does not handle cases where the attribute overwrites an existing attribute linkInsertBefore(alist, a, before); + // TODO see if adding this attribute leads to a fragmented contiguous run } From 4c99899a1d5baa7d68ff1ade47c5c95f7377138c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Dec 2016 23:07:48 -0500 Subject: [PATCH 019/487] More work. Much clearer now... not yet complete though. --- common/exp_attrdll.c | 103 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 45a30a24..51c1a90c 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -53,10 +53,36 @@ static void linkInsertBefore(struct attrlist *alist, struct attr *what, struct a a->prev->next = a; } +static int attrHasPos(struct attr *a, size_t pos) +{ + if (pos < a->start) + return 0; + return pos < a->end; +} + +// returns 1 if there was an intersection and 0 otherwise +static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) +{ + // is the range outside a entirely? + if (*start >= a->end) + return 0; + if (*end < a->start) + return 0; + + // okay, so there is an overlap + // compute the intersection + if (*start < a->start) + *start = a->start; + if (*end > a->end) + *end = a->end; + return 1; +} + +#if 0 void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; - struct attr *after; + struct attr *before; a = uiNew(struct attr); a->type = type; @@ -65,10 +91,81 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s a->end = end; // place a before the first element that starts after a does - for (before = alist->first; before != NULL; before = before->next) + for (before = alist->first; before != NULL; before = before->next) { + size_t lstart, lend; + if (before->start > a->start) break; - // TODO this does not handle cases where the attribute overwrites an existing attribute + // if this attribute overrides another, we have to split + lstart = start; + lend = end; + if (!attrRangeIntersect(before, &lstart, &lend)) + continue; + if (before->type != type) + continue; + if (before->val == val) + continue; + // TODO now split around start/end and drop the overlap + } linkInsertBefore(alist, a, before); // TODO see if adding this attribute leads to a fragmented contiguous run } +#endif + +void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +{ + struct attr *a; + struct attr *before; + struct attr *tail = NULL; + + // first, figure out where in the list this should go + // in addition, if this attribute overrides one that already exists, split that one apart so this one can take over + before = alist->first; + while (before != NULL) { + size_t lstart, lend; + + // once we get to the first point after start, we know where to insert + if (before->start > start) + break; + + // if we have already split a prior instance of this attribute, don't bother doing it again + if (tail != NULL) + goto next; + + // should we split this? + if (before->type != type) + goto next; + lstart = start; + lend = end; + if (!attrRangeIntersects(before, &lstart, &lend)) + goto next; + + // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything + // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? + if (before->val == val) { + // TODO + } + // okay the values are different; we need to split apart + // TODO + + next: + before = before->next; + } + + // if we got here, we know we have to add the attribute before before + a = uiNew(struct attr); + a->type = type; + a->val = val; + a->start = start; + a->end = end; + linkInsertBefore(alist, a, before); + + // and finally, if we split, insert the remainder + if (tail == NULL) + return; + // note we start at before; it won't be inserted before that by the sheer nature of how the code above works + for (; before != NULL; before = before->next) + if (before->start > tail->start) + break; + linkInsertBefore(alist, tail, before); +} From 75525196b114e69b86647c4f5232e7de4e2f5d9b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Dec 2016 11:49:54 -0500 Subject: [PATCH 020/487] Even more expansion of the experimental attribute list. --- common/exp_attrdll.c | 134 +++++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 31 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 51c1a90c..138b4643 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -15,7 +15,7 @@ struct attrlist { }; // if before is NULL, add to the end of the list -static void linkInsertBefore(struct attrlist *alist, struct attr *what, struct attr *before) +static void linkInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) { // if the list is empty, this is the first item if (alist->first == NULL) { @@ -78,45 +78,114 @@ static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) return 1; } -#if 0 -void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +static void attrUnlink(struct attrlist *alist, struct attr *a, struct attr **next) { - struct attr *a; - struct attr *before; + struct attr *p, *n; - a = uiNew(struct attr); - a->type = type; - a->val = val; - a->start = start; - a->end = end; + p = a->prev; + n = a->next; + a->prev = NULL; + a->next = NULL; - // place a before the first element that starts after a does - for (before = alist->first; before != NULL; before = before->next) { - size_t lstart, lend; - - if (before->start > a->start) - break; - // if this attribute overrides another, we have to split - lstart = start; - lend = end; - if (!attrRangeIntersect(before, &lstart, &lend)) - continue; - if (before->type != type) - continue; - if (before->val == val) - continue; - // TODO now split around start/end and drop the overlap + // only item in list? + if (p == NULL && n == NULL) { + alist->first = NULL; + alist->last = NULL; + *next = NULL; + return; } - linkInsertBefore(alist, a, before); - // TODO see if adding this attribute leads to a fragmented contiguous run + // start of list? + if (p == NULL) { + n->prev = NULL; + alist->first = n; + *next = n; + return; + } + // end of list? + if (n == NULL) { + p->next = NULL; + alist->last = p; + *next = NULL; + return; + } + // middle of list + p->next = n; + n->prev = p; + *next = n; +} + +static void attrDelete(struct attrlist *alist, struct attr *a, struct attr **next) +{ + attrUnlink(alist, a, next); + uiFree(a); +} + +// attrDropRange() removes attributes without deleting characters. +// +// If the attribute needs no change, 1 is returned. +// +// If the attribute needs to be deleted, it is deleted and 0 is returned. +// +// If the attribute only needs to be resized at the end, it is adjusted and 1 is returned. +// +// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail, and 1 is returned. +// +// Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. Then, 2 is returned. +// +// In all cases, next is set to the next attribute to look at in a forward sequential loop. +// +// TODO is the return value relevant? +static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail, struct attr **next) +{ + struct attr *b; + + // always pre-initialize tail to NULL + *tail = NULL; + + if (!attrRangeIntersect(a, &start, &end)) { + // out of range; nothing to do + *next = a->next; + return 1; + } + + // just outright delete the attribute? + if (a->start == start && a->end == end) { + attrDelete(alist, a, next); + return 0; + } + + // only chop off the start or end? + if (a->start == start) { // chop off the end + a->end = end; + *next = a->next; + return 1; + } + if (a->end == end) { // chop off the start + a->start = start; + attrUnlink(attr, a, next); + *tail = a; + return 1; + } + + // we'll need to split the attribute into two + b = uiNew(struct attr); + b->type = a->type; + b->val = a->val; + b->start = end; + b->end = a->end; + *tail = b; + + a->end = start; + *next = a->next; + return 2; } -#endif void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; struct attr *before; struct attr *tail = NULL; + int split = 0; // first, figure out where in the list this should go // in addition, if this attribute overrides one that already exists, split that one apart so this one can take over @@ -129,7 +198,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s break; // if we have already split a prior instance of this attribute, don't bother doing it again - if (tail != NULL) + if (split) goto next; // should we split this? @@ -144,9 +213,12 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? if (before->val == val) { // TODO + return; } // okay the values are different; we need to split apart - // TODO + attrDropRange(alist, a, start, end, &tail, &before); + split = 1; + continue; next: before = before->next; From 5c96266c7c69219bab7a88dd1488e9b17e679491 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Dec 2016 14:06:37 -0500 Subject: [PATCH 021/487] And finished implementing attrlistInsertAt(). Woo! --- common/exp_attrdll.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 138b4643..531a2f65 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -15,7 +15,7 @@ struct attrlist { }; // if before is NULL, add to the end of the list -static void linkInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) +static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) { // if the list is empty, this is the first item if (alist->first == NULL) { @@ -180,6 +180,29 @@ static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, s return 2; } +static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_t end) +{ + struct attr *before; + struct attr *unused; + + // adjusting the end is simple: if it ends before our new end, just set the new end + if (a->end < end) + a->end = end; + + // adjusting the start is harder + // if the start is before our new start, we are done + // otherwise, we have to move the start back AND reposition the attribute to keep the sorted order + if (a->start <= start) + return; + a->start = start; + // TODO could we just return next? + attrUnlink(alist, a, &unused); + for (before = alist->first; before != NULL; before = before->next) + if (before->start > a->start) + break; + attrInsertBefore(alist, a, before); +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -211,8 +234,9 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? + // TODO will this cause problems with fonts? if (before->val == val) { - // TODO + attrGrow(alist, a, start, end); return; } // okay the values are different; we need to split apart @@ -230,7 +254,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s a->val = val; a->start = start; a->end = end; - linkInsertBefore(alist, a, before); + attrInsertBefore(alist, a, before); // and finally, if we split, insert the remainder if (tail == NULL) @@ -239,5 +263,5 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s for (; before != NULL; before = before->next) if (before->start > tail->start) break; - linkInsertBefore(alist, tail, before); + attrInsertBefore(alist, tail, before); } From 96e15116ba0a8cb62f3ff49a0ce95ea1c221be54 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Dec 2016 14:11:12 -0500 Subject: [PATCH 022/487] Added some expository information about attribute lists. --- common/exp_attrdll.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 531a2f65..c4083e28 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -1,5 +1,13 @@ // 16 december 2016 +/* +An attribute list is a doubly linked list of attributes. +The list is kept sorted in increasing order by start position. Whether or not the sort is stable is undefined, so no temporal information should be expected to stay. +Overlapping attributes are not allowed; if an attribute is added that conflicts with an existing one, the existing one is removed. +In addition, the linked list tries to reduce fragmentation: if an attribute is added that just expands another, then there will only be one entry in alist, not two. (TODO does it really?) +The linked list is not a ring; alist->fist->prev == NULL and alist->last->next == NULL. +*/ + struct attr { uiAttribute type; uintptr_t val; @@ -235,6 +243,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? // TODO will this cause problems with fonts? + // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? if (before->val == val) { attrGrow(alist, a, start, end); return; From 7ebfe73bce8f913319c36894ca3dbb5f9f645f5a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 11:02:33 -0500 Subject: [PATCH 023/487] Some minor cleanup. --- common/exp_attrdll.c | 65 +++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index c4083e28..c9cd1c68 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -86,7 +86,8 @@ static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) return 1; } -static void attrUnlink(struct attrlist *alist, struct attr *a, struct attr **next) +// returns the old a->next, for forward iteration +static struct attr *attrUnlink(struct attrlist *alist, struct attr *a) { struct attr *p, *n; @@ -99,80 +100,73 @@ static void attrUnlink(struct attrlist *alist, struct attr *a, struct attr **nex if (p == NULL && n == NULL) { alist->first = NULL; alist->last = NULL; - *next = NULL; - return; + return NULL; } // start of list? if (p == NULL) { n->prev = NULL; alist->first = n; - *next = n; - return; + return n; } // end of list? if (n == NULL) { p->next = NULL; alist->last = p; - *next = NULL; - return; + return NULL; } // middle of list p->next = n; n->prev = p; - *next = n; + return n; } -static void attrDelete(struct attrlist *alist, struct attr *a, struct attr **next) +// returns the old a->next, for forward iteration +static struct attr *attrDelete(struct attrlist *alist, struct attr *a) { - attrUnlink(alist, a, next); + struct attr *next; + + next = attrUnlink(alist, a); uiFree(a); + return next; } // attrDropRange() removes attributes without deleting characters. // -// If the attribute needs no change, 1 is returned. +// If the attribute needs no change, then nothing is done. // -// If the attribute needs to be deleted, it is deleted and 0 is returned. +// If the attribute needs to be deleted, it is deleted. // -// If the attribute only needs to be resized at the end, it is adjusted and 1 is returned. +// If the attribute only needs to be resized at the end, it is adjusted. // -// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail, and 1 is returned. +// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail. // -// Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. Then, 2 is returned. +// Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. // -// In all cases, next is set to the next attribute to look at in a forward sequential loop. -// -// TODO is the return value relevant? -static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail, struct attr **next) +// In all cases, the return value is the next attribute to look at in a forward sequential loop. +static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail) { struct attr *b; // always pre-initialize tail to NULL *tail = NULL; - if (!attrRangeIntersect(a, &start, &end)) { + if (!attrRangeIntersect(a, &start, &end)) // out of range; nothing to do - *next = a->next; - return 1; - } + return a->next; // just outright delete the attribute? - if (a->start == start && a->end == end) { - attrDelete(alist, a, next); - return 0; - } + if (a->start == start && a->end == end) + return attrDelete(alist, a); // only chop off the start or end? if (a->start == start) { // chop off the end a->end = end; - *next = a->next; - return 1; + return a->next; } if (a->end == end) { // chop off the start a->start = start; - attrUnlink(attr, a, next); *tail = a; - return 1; + return attrUnlink(attr, a); } // we'll need to split the attribute into two @@ -184,14 +178,12 @@ static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, s *tail = b; a->end = start; - *next = a->next; - return 2; + return a->next; } static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_t end) { struct attr *before; - struct attr *unused; // adjusting the end is simple: if it ends before our new end, just set the new end if (a->end < end) @@ -203,8 +195,7 @@ static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_ if (a->start <= start) return; a->start = start; - // TODO could we just return next? - attrUnlink(alist, a, &unused); + attrUnlink(alist, a); for (before = alist->first; before != NULL; before = before->next) if (before->start > a->start) break; @@ -249,7 +240,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s return; } // okay the values are different; we need to split apart - attrDropRange(alist, a, start, end, &tail, &before); + before = attrDropRange(alist, a, start, end, &tail); split = 1; continue; From fee06b95532fb73d52096c17055e21baaddefe27 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 11:07:52 -0500 Subject: [PATCH 024/487] Wrote the prototypes for the other functions I will need. --- common/exp_attrdll.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index c9cd1c68..11e70d32 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -265,3 +265,25 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s break; attrInsertBefore(alist, tail, before); } + +void attrlistInsertCharacters(struct attrlist *alist, size_t start, size_t end) +{ +} + +// The attributes are those of character start - 1. +// If start == 0, the attributes are those of character 0. +void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t end) +{ +} + +void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) +{ +} + +void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) +{ +} + +void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +{ +} From 78f5ca5eb5ff0b44693144e160f52b8f88951e93 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 13:46:20 -0500 Subject: [PATCH 025/487] Implemented attrlistInsertCharactersUnattributed(). --- common/exp_attrdll.c | 72 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 11e70d32..0340d744 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -202,6 +202,29 @@ static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_ attrInsertBefore(alist, a, before); } +// returns the right side of the split, which is unlinked, or NULL if no split was done +static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t at) +{ + struct attr *b; + + // no splittng needed? + // note the equality: we don't need to split at start or end + // in the end case, the last split point is at - 1; at itself is outside the range, and at - 1 results in the right hand side having length 1 + if (at <= a->start) + return NULL; + if (at >= a->end) + return NULL; + + b = uiNew(struct attr); + b->type = a->type; + b->val = a->val; + b->start = at; + b->end = a->end; + + a->end = at; + return b; +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -266,13 +289,58 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s attrInsertBefore(alist, tail, before); } -void attrlistInsertCharacters(struct attrlist *alist, size_t start, size_t end) +void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count) { + struct attr *a; + struct attr *tails = NULL; + + // every attribute before the insertion point can either cross into the insertion point or not + // if it does, we need to split that attribute apart at the insertion point, keeping only the old attribute in place, adjusting the new tail, and preparing it for being re-added later + for (a = alist->first; a != NULL; a = a->next) { + struct attr *tail; + + // stop once we get to the insertion point + if (a->start >= start) + break; + // only do something if overlapping + if (!attrHasPos(a, start)) + continue; + + tail = attrSplitAt(alist, a, start); + // adjust the new tail for the insertion + tail->start += count; + tail->end += count; + // and queue it for re-adding later + // we can safely use tails as if it was singly-linked since it's just a temporary list; we properly merge them back in below and they'll be doubly-linked again then + // TODO actually we could probably save some time by making then doubly-linked now and adding them in one fell swoop, but that would make things a bit more complicated... + tail->next = tails; + tails = tail; + } + + // at this point in the attribute list, we are at or after the insertion point + // all the split-apart attributes will be at the insertion point + // therefore, we can just add them all back now, and the list will still be sorted correctly + while (tails != NULL) { + struct attr *next; + + // make all the links NULL before insertion, just to be safe + next = tails->next; + tails->next = NULL; + attrInsertBefore(alist, tails, a); + tails = next; + } + + // every remaining attribute will be either at or after the insertion point + // we just need to move them ahead + for (; a != NULL; a = a->next) { + a->start += count; + a->end += count; + } } // The attributes are those of character start - 1. // If start == 0, the attributes are those of character 0. -void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t end) +void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) { } From 643e2937c42110f35949c3debe191a48666ad36c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 17:15:48 -0500 Subject: [PATCH 026/487] More stuff I guess? --- common/exp_attrdll.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 0340d744..45717b54 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -342,6 +342,13 @@ void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, // If start == 0, the attributes are those of character 0. void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) { + size_t from; + + from = start - 1; + if (start == 0) + from = 0; + + TODO } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From 4f1ba0df84f990cb1dfe27869067166cf7465c48 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Dec 2016 14:24:26 -0500 Subject: [PATCH 027/487] Plotted out at a high level how insertion with attribute borrowing should work. --- common/exp_attrdll.c | 50 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 45717b54..372110ed 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -347,8 +347,54 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t from = start - 1; if (start == 0) from = 0; - - TODO +///////// + if start == 0 + for every attribute + if it doesn't start at 0 + move start up by count + move end up by count + return + for every attribute + if the attribute ends at or after the insertion point + advance its end by count + continue + if the attribute starts at or after the insertion point + advance its start and end by count + continue +///// +abcdefghi (012345678 9) +red start 0 end 3 +bold start 2 end 6 +underline start 5 end 8 +inserting qwe -> qweabcdefghi (0123456789AB C) +012345678 9 +rrr------ +--bbbb--- +-----uuu- +before 0, 1, 2 (grow the red part, move everything else down) + red -> start 0 (no change) end 3+3=6 + bold -> start 2+3=5 end 6+3=9 + underline -> start 5+3=8 end 8+3=B +before 3 (grow red and bold, move underline down) + red -> start 0 (no change) end 3+3=6 + bold -> start 2 (no change) end 6+3=9 + underline -> start 5+3=8 end 8+3=B +before 4, 5 (keep red, grow bold, move underline down) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6+3=9 + underline -> start 5+3=8 end 8+3=B +before 6 (keep red, grow bold and underline) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6+3=9 + underline -> start 5 (no change) end 8+3=B +before 7, 8 (keep red and bold, grow underline) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6 (no change) + underline -> start 5 (no change) end 8+3=B +before 9 (keep all three) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6 (no change) + underline -> start 5 (no change) end 8 (no change) } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From 28ca02673b01e035828f3555dc2aa9078dd9e9ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Dec 2016 14:47:11 -0500 Subject: [PATCH 028/487] Even more trying to reason about the algorithm. --- common/exp_attrdll.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 372110ed..e03aef99 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -348,20 +348,13 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t if (start == 0) from = 0; ///////// - if start == 0 - for every attribute - if it doesn't start at 0 - move start up by count - move end up by count - return for every attribute - if the attribute ends at or after the insertion point - advance its end by count - continue - if the attribute starts at or after the insertion point - advance its start and end by count - continue -///// + if TODO + adjust start by count + adjust end by count + else if TODO + adjust end by count +///////// abcdefghi (012345678 9) red start 0 end 3 bold start 2 end 6 @@ -395,6 +388,16 @@ before 9 (keep all three) red -> start 0 (no change) end 3 (no change) bold -> start 2 (no change) end 6 (no change) underline -> start 5 (no change) end 8 (no change) +result: + 0 1 2 3 4 5 6 7 8 9 + red E E E e n n n n n n + bold s s S E E E e n n n +underline s s s s s S E E e n +N = none +E = end only +S = start and end +uppercase = in original range, lowercase = not +*/ } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From ecc7b70a6bcfd10acaaad1d69ff9784873850326 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Dec 2016 16:16:52 -0500 Subject: [PATCH 029/487] And FINALLY implemented that function :D --- common/exp_attrdll.c | 62 ++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index e03aef99..2949e15b 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -340,30 +340,22 @@ void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, // The attributes are those of character start - 1. // If start == 0, the attributes are those of character 0. -void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) -{ - size_t from; +/* +This is an obtuse function. Here's some diagrams to help. + +Given the input string + abcdefghi (positions: 012345678 9) +and attribute set + red start 0 end 3 + bold start 2 end 6 + underline start 5 end 8 +or visually: + 012345678 9 + rrr------ + --bbbb--- + -----uuu- +If we insert qwe to result in positions 0123456789AB C: - from = start - 1; - if (start == 0) - from = 0; -///////// - for every attribute - if TODO - adjust start by count - adjust end by count - else if TODO - adjust end by count -///////// -abcdefghi (012345678 9) -red start 0 end 3 -bold start 2 end 6 -underline start 5 end 8 -inserting qwe -> qweabcdefghi (0123456789AB C) -012345678 9 -rrr------ ---bbbb--- ------uuu- before 0, 1, 2 (grow the red part, move everything else down) red -> start 0 (no change) end 3+3=6 bold -> start 2+3=5 end 6+3=9 @@ -388,6 +380,7 @@ before 9 (keep all three) red -> start 0 (no change) end 3 (no change) bold -> start 2 (no change) end 6 (no change) underline -> start 5 (no change) end 8 (no change) + result: 0 1 2 3 4 5 6 7 8 9 red E E E e n n n n n n @@ -397,7 +390,30 @@ N = none E = end only S = start and end uppercase = in original range, lowercase = not + +which results in our algorithm: + for each attribute + if start < insertion point + move start up + else if start == insertion point + if start != 0 + move start up + if end <= insertion point + move end up */ +// TODO does this ensure the list remains sorted? +void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) +{ + struct attr *a; + + for (a = alist->first; a != NULL; a = a->next) { + if (a->start < start) + a->start += count; + else if (a->start == start && start != 0) + a->start += count; + if (a->end <= start) + a->end += count; + } } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From 0078615662419e9a08acfa3dcb9d959b77f20ac3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Dec 2016 13:05:36 -0500 Subject: [PATCH 030/487] More attribute list work. --- common/exp_attrdll.c | 46 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 2949e15b..e0a20cc0 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -2,6 +2,7 @@ /* An attribute list is a doubly linked list of attributes. +Attribute start positions are inclusive and attribute end positions are exclusive (or in other words, [start, end)). The list is kept sorted in increasing order by start position. Whether or not the sort is stable is undefined, so no temporal information should be expected to stay. Overlapping attributes are not allowed; if an attribute is added that conflicts with an existing one, the existing one is removed. In addition, the linked list tries to reduce fragmentation: if an attribute is added that just expands another, then there will only be one entry in alist, not two. (TODO does it really?) @@ -251,7 +252,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s goto next; lstart = start; lend = end; - if (!attrRangeIntersects(before, &lstart, &lend)) + if (!attrRangeIntersect(before, &lstart, &lend)) goto next; // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything @@ -416,8 +417,51 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t } } +// TODO replace at point with — replaces with first character's attributes + void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) { + struct attr *a; + struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tailsAt = NULL; + + a = alist->start; + while (a != NULL) { + size_t lstart, lend; + + // this defines where to re-attach the tails + // (all the tails will have their start at end, so we can just insert them all before tailsAt) + if (a->start >= end) { + tailsAt = a; + // and at this point we're done, so + break; + } + if (a->type != type) + goto next; + lstart = start; + lend = end; + if (!attrRangeIntersect(a, &lstart, &lend)) + goto next; + a = attrDropRange(alist, a, start, end, &tail); + if (tail != NULL) { + tail->next = tails; + tails = tail; + } + continue; + + next: + a = a->next; + } + + while (tails != NULL) { + struct attr *next; + + // make all the links NULL before insertion, just to be safe + next = tails->next; + tails->next = NULL; + attrInsertBefore(alist, tails, a); + tails = next; + } } void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) From ab8aa9266ec882f6377934411cbe41a658fb44a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Dec 2016 14:22:01 -0500 Subject: [PATCH 031/487] Finished filling in exp_attrdll.c. I think this will stay. --- common/exp_attrdll.c | 118 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index e0a20cc0..d8c9152d 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -156,19 +156,23 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t return a->next; // just outright delete the attribute? - if (a->start == start && a->end == end) + // the inequalities handle attributes entirely inside the range + // if both are equal, the attribute's range is equal to the range + if (a->start >= start && a->end <= end) return attrDelete(alist, a); // only chop off the start or end? - if (a->start == start) { // chop off the end - a->end = end; - return a->next; - } - if (a->end == end) { // chop off the start - a->start = start; + if (a->start == start) { // chop off the start + // we are dropping the left half, so set a->start and unlink + a->start = end; *tail = a; return attrUnlink(attr, a); } + if (a->end == end) { // chop off the end + // we are dropping the right half, so just set a->end + a->end = start; + return a->next; + } // we'll need to split the attribute into two b = uiNew(struct attr); @@ -226,6 +230,61 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a return b; } +// attrDeleteRange() removes attributes while deleting characters. +// +// If the attribute does not include the deleted range, then nothing is done (though the start and end are adjusted as necessary). +// +// If the attribute needs to be deleted, it is deleted. +// +// Otherwise, the attribute only needs the start or end deleted, and it is adjusted. +// +// In all cases, the return value is the next attribute to look at in a forward sequential loop. +// TODO rewrite this comment +static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size_t start, size_t end) +{ + size_t ostart, oend; + size_t count; + + ostart = start; + oend = end; + count = oend - ostart; + + if (!attrRangeIntersect(a, &start, &end)) { + // out of range + // adjust if necessary + if (a->start >= ostart) + a->start -= count; + if (a->end >= oend) + a->end -= count; + return a->next; + } + + // just outright delete the attribute? + // the inequalities handle attributes entirely inside the range + // if both are equal, the attribute's range is equal to the range + if (a->start >= start && a->end <= end) + return attrDelete(alist, a); + + // only chop off the start or end? + if (a->start == start) { // chop off the start + // if we weren't adjusting positions this would just be setting a->start to end + // but since this is deleting from the start, we need to adjust both by count + a->start = end - count; + a->end -= count; + return a->next; + } + if (a->end == end) { // chop off the end + // a->start is already good + a->end = start; + return a->next; + } + + // in this case, the deleted range is inside the attribute + // we can clear it by just removing count from a->end + a->end -= count; + return a->next; +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -464,10 +523,55 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st } } +// TODO merge this with the above void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) { + struct attr *a; + struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tailsAt = NULL; + + a = alist->start; + while (a != NULL) { + size_t lstart, lend; + + // this defines where to re-attach the tails + // (all the tails will have their start at end, so we can just insert them all before tailsAt) + if (a->start >= end) { + tailsAt = a; + // and at this point we're done, so + break; + } + lstart = start; + lend = end; + if (!attrRangeIntersect(a, &lstart, &lend)) + goto next; + a = attrDropRange(alist, a, start, end, &tail); + if (tail != NULL) { + tail->next = tails; + tails = tail; + } + continue; + + next: + a = a->next; + } + + while (tails != NULL) { + struct attr *next; + + // make all the links NULL before insertion, just to be safe + next = tails->next; + tails->next = NULL; + attrInsertBefore(alist, tails, a); + tails = next; + } } void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) { + struct attr *a; + + a = alist->first; + while (a != NULL) + a = attrDeleteRange(alist, a, start, end); } From 5c1dfbd86f8ad70653296b0e8fcee1e316fc8597 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Dec 2016 14:33:30 -0500 Subject: [PATCH 032/487] Got rid of the old attribute code and integrated exp_attrdll.c into attrstr.c. I'll rename the file to attrlist.c later. --- common/attrstr.c | 311 +------------------------------------------ common/exp_attrdll.c | 20 ++- 2 files changed, 24 insertions(+), 307 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 224b6074..822e55bb 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -6,9 +6,7 @@ struct uiAttributedString { char *s; size_t len; - size_t nAttrs; // TODO this needs to be maintained; is it necessary? - struct attr *attrs; - struct attr *lastattr; + struct attrlist *attrs; // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator // this ensures no one platform has a speed advantage (sorry GTK+) @@ -22,299 +20,6 @@ struct uiAttributedString { struct graphemes *graphemes; }; -struct attr { - uiAttribute type; - uintptr_t val; - size_t start; - size_t end; - struct attr *next; -}; - -// if new entries types are added to the end of the uiAttribute enumeration, this MUST be updated! -#define nAttrTypes (TODO + 1) - -static int attrHasPos(struct attr *a, size_t pos) -{ - if (pos < a->start) - return 0; - return pos < a->end; -} - -// returns 1 if there was an intersection and 0 otherwise -static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) -{ - // is the range outside a entirely? - if (*start >= a->end) - return 0; - if (*end < a->start) - return 0; - - // okay, so there is an overlap - // compute the intersection - if (*start < a->start) - *start = a->start; - if (*end > a->end) - *end = a->end; - return 1; -} - -// returns: -// - 0 if no change needed -// - 1 if the attribute was split -static int attrSplitAt(uiAttributedString *s, struct attr *a, size_t at) -{ - struct attr *b; - - // no splittng needed? - if (at == a->start) - return 0; - if ((at + 1) == a->end) - return 0; - - b = uiNew(struct attr); - b->what = a->what; - b->val = a->val; - b->start = at; - b->end = a->end; - b->next = a->next; - - a->end = at; - a->next = b; - if (a == s->lastattr) - s->lastattr = a->next; - return 1; -} - -// removes attributes without deleting characters -// returns: -// - 0 if the attribute needs to be deleted -// - 1 if the attribute was changed or no change needed -// - 2 if the attribute was split -static int attrDropRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) -{ - struct attr *b; - - if (!attrRangeIntersect(a, &start, &end)) - // out of range; nothing to do - return 1; - - // just outright delete the attribute? - if (a->start == start && a->end == end) - return 0; - - // only chop off the start or end? - if (a->start == start) { // chop off the end - a->end = end; - return 1; - } - if (a->end == end) { // chop off the start - a->start = start; - return 1; - } - - // we'll need to split the attribute into two - b = uiNew(struct attr); - b->what = a->what; - b->val = a->val; - b->start = end; - b->end = a->end; - b->next = a->next; - - a->end = start; - a->next = b; - if (a == s->lastattr) - s->lastattr = a->next; - return 2; -} - -// removes attributes while deleting characters -// returns: -// - 0 if the attribute needs to be deleted -// - 1 if the attribute was changed or no change needed -// - 2 if the attribute was split -static int attrDeleteRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) -{ - struct attr *b; - struct attr tmp; - size_t count, acount; - - if (!attrRangeIntersect(a, &start, &end)) - // out of range; nothing to do - return 1; - - // just outright delete the attribute? - if (a->start == start && a->end == end) - return 0; - - acount = a->end - a->start; - count = end - start; - - // only the start or end deleted? - if (a->start == start) { // start deleted - a->end = a->start + (acount - count); - return 1; - } - if (a->end == end) { // end deleted - a->end = a->start + count; - return 1; - } - - // something in the middle deleted - // we ened to split the attribute into *three* - // first, split at the start of he deleted range - tmp.what = a->what; - tmp.val = a->val; - tmp.start = start; - tmp.end = a->end; - tmp.next = a->next; - - a->end = start; - a->next = &tmp; - - // now split at the end - b = uiNew(struct attr); - b->what = a->what; - b->val = a->val; - b->start = end; - b->end = a->end; - b->next = tmp.next; - - tmp.end = end; - tmp.next = b; - - // and now push b back to overwrite the deleted stuff - a->next = b; - b->start -= count; - b->end -= count; - if (a == s->lastattr) - s->lastattr = a->next; - return 2; -} - -// returns the attribute to continue with -static struct attr *attrDelete(uiAttributedString *s, struct attr *a, struct attr *prev) -{ - if (a == s->attrs) { - s->attrs = a->next; - uiFree(a); - return s->attrs; - } - if (a == s->lastattr) - s->lastattr = prev; - prev->next = a->next; - uiFree(a); - return prev->next; -} - -static void attrAppend(uiAttributedString *s, int type, uintptr_t val, size_t start, size_t end) -{ - struct attr *a; - - a = uiNew(struct attr); - a->type = type; - a->val = val; - a->start = start; - a->end = end; - if (s->attrs == NULL) { - s->attrs = a; - s->lastattr = a; - return; - } - s->lastattr->next = a; - s->lastattr = a; -} - -// alist should be struct attr *alist[nAttrTypes] -static void attrsGetFor(uiAttributedString *s, struct attr **alist, size_t at) -{ - int i; - struct attr *a; - - // we want the attributes for at - // these are the attributes of at - 1 - // if at == 0. then these are the attributes at 0 - if (at != 0) - at--; - - // make usre unset attributes are NULL - for (i = 0; i < nAttrTypes; i++) - alist[i] = NULL; - - for (a = s->attrs; a != NULL; a = a->next) { - if (!attrHasPos(a, at)) - continue; - alist[a->type] = a; - } -} - -// TODO have a routine that prunes overridden attributes for a given character from the list? merge it with the above? - -static void attrAdjustPostInsert(uiAttributedString *s, size_t start, size_t n, size_t oldlen) -{ - struct attr *a; - - for (a = s->attrs; a != NULL; a = a->next) { - size_t astart, aend; - int splitNeeded; - - // do we need to adjust this, and where? - astart = start; - aend = oldlen; - if (!attrRangeIntersect(a, &astart, &aend)) - continue; - - // if only part of the attribute falls in the moved area, we need to split at the insertion point and adjust both resultant attributes - // otherwise, we only adjust the original attribute - // split *before* adjusting so that the split is correct - splitNeeded = attrSplit(s, a, astart); - if (a->start >= start) - a->start += n; - if (a->end >= end) - a->end += n; - if (splitNeeded == 1) { - a = a->next; - if (a->start >= start) - a->start += n; - if (a->end >= end) - a->end += n; - } - } -} - -static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end) -{ - struct attr *a, *prev; - - a = s->attrs; - prev = NULL; - while (a != NULL) { - size_t astart, aend; - - // do we need to adjust this, and where? - astart = start; - aend = end; - if (!attrRangeIntersect(a, &astart, &aend)) { - prev = a; - a = a->next; - continue; - } - - switch (attrDeleteRange(s, a, astart, aend)) { - case 0: // delete - a = attrDelete(s, a, prev); - // keep prev unchanged - break; - case 2: // split - a = a->next; - // fall through - case 1: // existing only needed adjustment - prev = a; - a = a->next; - break; - } - } -} - static void resize(uiAttributedString *s, size_t u8, size_t u16) { s->len = u8; @@ -330,6 +35,7 @@ uiAttributedString *uiNewAttributedString(const char *initialString) uiAttributedString *s; s = uiNew(uiAttributedString); + s->attrs = attrlistNew(); uiAttributedStringAppendUnattributed(s, initialString); return s; } @@ -358,14 +64,7 @@ static void invalidateGraphemes(uiAttributedString *s) void uiFreeAttributedString(uiAttributedString *s) { - struct attr *a, *b; - - a = s->attrs; - while (a != NULL) { - b = a->next; - uiFree(a); - a = b; - } + attrlistFree(s->attrs); invalidateGraphemes(s); uiFree(s->u16tou8); uiFree(s->u8tou16); @@ -511,7 +210,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u16tou8[at16 + n16 + i] += n8; // and finally do the attributes - attrAdjustPostInsert(s, at, n8, oldlen); + attrlistInsertCharactersUnattributed(s->attrs, at, n8); } // TODO document that end is the first index that will be maintained @@ -566,7 +265,7 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) s->u16[start16 + count16] = 0; // fix up attributes - attrAdjustPostDelete(s, start, end); + attrlistRemoveCharacters(s->attrs, start, end); // and finally resize resize(s, start + count, start16 + count16); diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index d8c9152d..5dc2fc9b 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -285,7 +285,7 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; struct attr *before; @@ -575,3 +575,21 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) while (a != NULL) a = attrDeleteRange(alist, a, start, end); } + +struct attrlist *attrlistNew(void) +{ + return uiNew(struct attrlist); +} + +void attrlistFree(struct attrlist *alist) +{ + struct attr *a, *next; + + a = alist->first; + while (a != NULL) { + next = a->next; + uiFree(a); + a = next; + } + uiFree(alist); +} From 4f6ed98e40a2aa04d6fdd74a043a3bac751a4dd3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Dec 2016 00:31:11 -0500 Subject: [PATCH 033/487] And integrated the new attribute list implementation fully. --- common/{exp_attrdll.c => attrlist.c} | 2 ++ common/uipriv.h | 12 ++++++++++++ 2 files changed, 14 insertions(+) rename common/{exp_attrdll.c => attrlist.c} (99%) diff --git a/common/exp_attrdll.c b/common/attrlist.c similarity index 99% rename from common/exp_attrdll.c rename to common/attrlist.c index 5dc2fc9b..4f3afd24 100644 --- a/common/exp_attrdll.c +++ b/common/attrlist.c @@ -1,4 +1,6 @@ // 16 december 2016 +#include "../ui.h" +#include "uipriv.h" /* An attribute list is a doubly linked list of attributes. diff --git a/common/uipriv.h b/common/uipriv.h index 1fe00b16..f29f4f86 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -64,6 +64,18 @@ struct graphemes { extern int graphemesTakesUTF16(void); extern struct graphemes *graphemes(void *s, size_t len); +// attrlist.c +struct attrlist; +extern void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end); +extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); +extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); +extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); +extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); +extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +// TODO move these to the top like everythng else +extern struct attrlist *attrlistNew(void); +extern void attrlistFree(struct attrlist *alist); + #ifdef __cplusplus } #endif From dfffc4c851db7c39398df7cdc3070ba2e4723ac4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Dec 2016 12:24:20 -0500 Subject: [PATCH 034/487] Started the API definition of the new uiDrawTextLayout. --- common/ui_attrstr.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h index a333fb78..f22fb3a4 100644 --- a/common/ui_attrstr.h +++ b/common/ui_attrstr.h @@ -28,3 +28,48 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +typedef struct uiDrawTextLayout uiDrawTextLayout; +typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; + +struct uiDrawTextLayoutLineMetrics { + double X; + double Y; + double Width; + double Ascent; + double Descent; + double Leading; +}; + +_UI_ENUM(uiDrawTextLayoutHitTestResult) { + uiDrawTextLayoutHitTestResultNowhere, + uiDrawTextLayoutHitTestResultOnLineTrailingWhitespace, + uiDrawTextLayoutHitTestResultOnCharacter, +}; + +struct uiDrawTextLayoutByteRangeRectangle { + int Line; + double X; + double Y; + double Width; + double Height; + size_t RealStart; + size_t RealEnd; +}; + +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, /* TODO default font */, double width); +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); +_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); +// TODO redo this? remove it entirely? +_UI_EXTERN void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height); +// TODO partial offset? +_UI_EXTERN uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line); +_UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); +// TODO draw only a line? +// TODO other layout-specific attributes (alignment, wrapping, etc.)? +// TODO number of lines visible for clipping rect, range visible for clipping rect? From e409943a50062890c7221103cb226a10d6621c95 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Dec 2016 14:01:09 -0500 Subject: [PATCH 035/487] One more support method before I can actually implement uiDrawTextLayout. Still need to figure out how I'm going to deal with fonts... --- common/attrlist.c | 11 +++++++++++ common/attrstr.c | 6 ++++++ common/ui_attrstr.h | 3 +++ common/uipriv.h | 3 ++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/common/attrlist.c b/common/attrlist.c index 4f3afd24..bae1203b 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -578,6 +578,17 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) a = attrDeleteRange(alist, a, start, end); } +void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +{ + struct attr *a; + + for (a = alist->first; a != NULL; a = a->next) + // TODO document this + // TODO should this be return 0 to break? + if ((*f)(s, a->type, a->val, a->start, a->end, data)) + break; +} + struct attrlist *attrlistNew(void) { return uiNew(struct attrlist); diff --git a/common/attrstr.c b/common/attrstr.c index 822e55bb..4cd447f6 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -294,3 +294,9 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) pos = s->u16tou8[pos]; return pos; } + +// TODO introduce an iterator? +void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +{ + attrlistForEach(s->attrs, s, f, data); +} diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h index f22fb3a4..f623612d 100644 --- a/common/ui_attrstr.h +++ b/common/ui_attrstr.h @@ -4,6 +4,8 @@ _UI_ENUM(uiAttribute) { // TODO }; +typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); + // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from // initialString. The string will be entirely unattributed. @@ -28,6 +30,7 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); +_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; diff --git a/common/uipriv.h b/common/uipriv.h index f29f4f86..70615d8f 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -71,7 +71,8 @@ extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); +extern void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // TODO move these to the top like everythng else extern struct attrlist *attrlistNew(void); extern void attrlistFree(struct attrlist *alist); From 30ca879c14fbdc7a8348d0b08aae9af16d417d4b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 2 Jan 2017 20:11:15 -0500 Subject: [PATCH 036/487] More API stuff. --- common/CMakeLists.txt | 2 ++ common/ui_attrstr.h | 58 ++++++++++++++++++++++++++++++++++++++++++- ui.h | 32 ------------------------ 3 files changed, 59 insertions(+), 33 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a4008fd1..fdace350 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,6 +1,8 @@ # 3 june 2016 list(APPEND _LIBUI_SOURCES + common/attrlist.c + common/attrstr.c common/areaevents.c common/control.c common/debug.c diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h index f623612d..7bfa0af9 100644 --- a/common/ui_attrstr.h +++ b/common/ui_attrstr.h @@ -1,7 +1,12 @@ typedef struct uiAttributedString uiAttributedString; _UI_ENUM(uiAttribute) { + // TODO uiAttributeFamily, + // TODO uiAttributeSize, + uiAttributeWeight, // TODO + // TODO uiAttributeSystem, + // TODO uiAttributeCustom, }; typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); @@ -32,6 +37,53 @@ _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, s _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; + +// TODO Minimum == 1? IIRC there is at least one font on OS X that actually has a weight of 0 +// TODO Maximum == 999? IIRC there is at least one font on OS X that actually has a weight of 1000 +_UI_ENUM(uiDrawTextWeight) { + uiDrawTextWeightMinimum = 0, + uiDrawTextWeightThin = 100, + uiDrawTextWeightUltraLight = 200, + uiDrawTextWeightLight = 300, + uiDrawTextWeightBook = 350, + uiDrawTextWeightNormal = 400, + uiDrawTextWeightMedium = 500, + uiDrawTextWeightSemiBold = 600, + uiDrawTextWeightBold = 700, + uiDrawTextWeightUltraBold = 800, + uiDrawTextWeightHeavy = 900, + uiDrawTextWeightUltraHeavy = 950, + uiDrawTextWeightMaximum = 1000, +}; + +_UI_ENUM(uiDrawTextItalic) { + uiDrawTextItalicNormal, + uiDrawTextItalicOblique, + uiDrawTextItalicItalic, +}; + +// TODO realign this so that Normal == 0? +_UI_ENUM(uiDrawTextStretch) { + uiDrawTextStretchUltraCondensed, + uiDrawTextStretchExtraCondensed, + uiDrawTextStretchCondensed, + uiDrawTextStretchSemiCondensed, + uiDrawTextStretchNormal, + uiDrawTextStretchSemiExpanded, + uiDrawTextStretchExpanded, + uiDrawTextStretchExtraExpanded, + uiDrawTextStretchUltraExpanded, +}; + +struct uiDrawFontDescriptor { + char *Family; + double Size; + uiDrawTextWeight Weight; + uiDrawTextItalic Italic; + uiDrawTextStretch Stretch; +}; + typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; @@ -61,7 +113,11 @@ struct uiDrawTextLayoutByteRangeRectangle { size_t RealEnd; }; -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, /* TODO default font */, double width); +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); diff --git a/ui.h b/ui.h index 5a2069e8..919f43d3 100644 --- a/ui.h +++ b/ui.h @@ -491,38 +491,6 @@ typedef struct uiDrawTextFont uiDrawTextFont; typedef struct uiDrawTextFontDescriptor uiDrawTextFontDescriptor; typedef struct uiDrawTextFontMetrics uiDrawTextFontMetrics; -_UI_ENUM(uiDrawTextWeight) { - uiDrawTextWeightThin, - uiDrawTextWeightUltraLight, - uiDrawTextWeightLight, - uiDrawTextWeightBook, - uiDrawTextWeightNormal, - uiDrawTextWeightMedium, - uiDrawTextWeightSemiBold, - uiDrawTextWeightBold, - uiDrawTextWeightUltraBold, - uiDrawTextWeightHeavy, - uiDrawTextWeightUltraHeavy, -}; - -_UI_ENUM(uiDrawTextItalic) { - uiDrawTextItalicNormal, - uiDrawTextItalicOblique, - uiDrawTextItalicItalic, -}; - -_UI_ENUM(uiDrawTextStretch) { - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchExtraCondensed, - uiDrawTextStretchCondensed, - uiDrawTextStretchSemiCondensed, - uiDrawTextStretchNormal, - uiDrawTextStretchSemiExpanded, - uiDrawTextStretchExpanded, - uiDrawTextStretchExtraExpanded, - uiDrawTextStretchUltraExpanded, -}; - struct uiDrawTextFontDescriptor { const char *Family; double Size; From 31274bcbd2b26a33bcdfde002b3e3a0afbb62980 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 2 Jan 2017 23:53:31 -0500 Subject: [PATCH 037/487] Started implementing the new attributed string system on OS X. --- common/attrstr.c | 12 + common/uipriv.h | 4 + darwin/_old_drawtext.m | 655 +++++++++++++++++++++++++++ darwin/drawtext.m | 660 ++-------------------------- ui.h | 40 +- common/ui_attrstr.h => ui_attrstr.h | 1 + 6 files changed, 720 insertions(+), 652 deletions(-) create mode 100644 darwin/_old_drawtext.m rename common/ui_attrstr.h => ui_attrstr.h (98%) diff --git a/common/attrstr.c b/common/attrstr.c index 4cd447f6..102d56eb 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -300,3 +300,15 @@ void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStrin { attrlistForEach(s->attrs, s, f, data); } + +// helpers for platform-specific code + +const uint16_t *attrstrUTF16(uiAttributedString *s) +{ + return s->u16; +} + +size_t attrstrUTF16LEn(uiAttributedString *s) +{ + return s->u16len; +} diff --git a/common/uipriv.h b/common/uipriv.h index 70615d8f..31d941ff 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -64,6 +64,10 @@ struct graphemes { extern int graphemesTakesUTF16(void); extern struct graphemes *graphemes(void *s, size_t len); +// attrstr.c +extern const uint16_t *attrstrUTF16(uiAttributedString *s); +extern size_t attrstrUTF16LEn(uiAttributedString *s); + // attrlist.c struct attrlist; extern void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end); diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m new file mode 100644 index 00000000..c376536a --- /dev/null +++ b/darwin/_old_drawtext.m @@ -0,0 +1,655 @@ +// 6 september 2015 +#import "uipriv_darwin.h" + +// TODO +#define complain(...) implbug(__VA_ARGS__) + +// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) +struct uiDrawFontFamilies { + CFArrayRef fonts; +}; + +uiDrawFontFamilies *uiDrawListFontFamilies(void) +{ + uiDrawFontFamilies *ff; + + ff = uiNew(uiDrawFontFamilies); + ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); + if (ff->fonts == NULL) + implbug("error getting available font names (no reason specified) (TODO)"); + return ff; +} + +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +{ + return CFArrayGetCount(ff->fonts); +} + +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) +{ + CFStringRef familystr; + char *family; + + familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); + // toll-free bridge + family = uiDarwinNSStringToText((NSString *) familystr); + // Get Rule means we do not free familystr + return family; +} + +void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) +{ + CFRelease(ff->fonts); + uiFree(ff); +} + +struct uiDrawTextFont { + CTFontRef f; +}; + +uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) +{ + uiDrawTextFont *font; + + font = uiNew(uiDrawTextFont); + font->f = f; + if (retain) + CFRetain(font->f); + return font; +} + +uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) +{ + // toll-free bridging; we do retain, though + return mkTextFont((CTFontRef) f, YES); +} + +static CFMutableDictionaryRef newAttrList(void) +{ + CFMutableDictionaryRef attr; + + attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (attr == NULL) + complain("error creating attribute dictionary in newAttrList()()"); + return attr; +} + +static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) +{ + CFStringRef cfstr; + + cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); + if (cfstr == NULL) + complain("error creating font family name CFStringRef in addFontFamilyAttr()"); + CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); + CFRelease(cfstr); // dictionary holds its own reference +} + +static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) +{ + CFNumberRef n; + + n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); + CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); + CFRelease(n); +} + +#if 0 +TODO +// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do +// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D +static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) +{ + CFMutableArrayRef outerArray; + CFMutableDictionaryRef innerDict; + CFNumberRef numType, numSelector; + int num; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) + complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); + + // Apple's headers say these are deprecated, but a few fonts still rely on them + num = kLetterCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + // these are the non-deprecated versions of the above; some fonts have these instead + num = kLowerCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kLowerCaseSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); + CFRelease(outerArray); +} +#endif + +// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( +// kode54 got these for me before I had access to El Capitan; thanks to him. +#define ourNSFontWeightUltraLight -0.800000 +#define ourNSFontWeightThin -0.600000 +#define ourNSFontWeightLight -0.400000 +#define ourNSFontWeightRegular 0.000000 +#define ourNSFontWeightMedium 0.230000 +#define ourNSFontWeightSemibold 0.300000 +#define ourNSFontWeightBold 0.400000 +#define ourNSFontWeightHeavy 0.560000 +#define ourNSFontWeightBlack 0.620000 +static const CGFloat ctWeights[] = { + // yeah these two have their names swapped; blame Pango + [uiDrawTextWeightThin] = ourNSFontWeightUltraLight, + [uiDrawTextWeightUltraLight] = ourNSFontWeightThin, + [uiDrawTextWeightLight] = ourNSFontWeightLight, + // for this one let's go between Light and Regular + // we're doing nearest so if there happens to be an exact value hopefully it's close enough + [uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2), + [uiDrawTextWeightNormal] = ourNSFontWeightRegular, + [uiDrawTextWeightMedium] = ourNSFontWeightMedium, + [uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold, + [uiDrawTextWeightBold] = ourNSFontWeightBold, + // for this one let's go between Bold and Heavy + [uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), + [uiDrawTextWeightHeavy] = ourNSFontWeightHeavy, + [uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack, +}; + +// Unfortunately there are still no named constants for these. +// Let's just use normalized widths. +// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. +// TODO verify this is correct +static const CGFloat ctStretches[] = { + [uiDrawTextStretchUltraCondensed] = -1.0, + [uiDrawTextStretchExtraCondensed] = -0.75, + [uiDrawTextStretchCondensed] = -0.5, + [uiDrawTextStretchSemiCondensed] = -0.25, + [uiDrawTextStretchNormal] = 0.0, + [uiDrawTextStretchSemiExpanded] = 0.25, + [uiDrawTextStretchExpanded] = 0.5, + [uiDrawTextStretchExtraExpanded] = 0.75, + [uiDrawTextStretchUltraExpanded] = 1.0, +}; + +struct closeness { + CFIndex index; + CGFloat weight; + CGFloat italic; + CGFloat stretch; + CGFloat distance; +}; + +// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. +// We have to implement the closest match ourselves. +// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those. +CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch) +{ + CGFloat targetWeight; + CGFloat italicCloseness, obliqueCloseness, normalCloseness; + CGFloat targetStretch; + CFArrayRef matching; + CFIndex i, n; + struct closeness *closeness; + CTFontDescriptorRef current; + CTFontDescriptorRef out; + + targetWeight = ctWeights[weight]; + switch (italic) { + case uiDrawTextItalicNormal: + italicCloseness = 1; + obliqueCloseness = 1; + normalCloseness = 0; + break; + case uiDrawTextItalicOblique: + italicCloseness = 0.5; + obliqueCloseness = 0; + normalCloseness = 1; + break; + case uiDrawTextItalicItalic: + italicCloseness = 0; + obliqueCloseness = 0.5; + normalCloseness = 1; + break; + } + targetStretch = ctStretches[stretch]; + + matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); + if (matching == NULL) + // no matches; give the original back and hope for the best + return against; + n = CFArrayGetCount(matching); + if (n == 0) { + // likewise + CFRelease(matching); + return against; + } + + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + for (i = 0; i < n; i++) { + CFDictionaryRef traits; + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + + closeness[i].index = i; + + current = CFArrayGetValueAtIndex(matching, i); + traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); + if (traits == NULL) { + // couldn't get traits; be safe by ranking it lowest + // LONGTERM figure out what the longest possible distances are + closeness[i].weight = 3; + closeness[i].italic = 2; + closeness[i].stretch = 3; + continue; + } + + symbolic = 0; // assume no symbolic traits if none are listed + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum != NULL) { + SInt32 s; + + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) + complain("error getting symbolic traits in matchTraits()"); + symbolic = (CTFontSymbolicTraits) s; + // Get rule; do not release cfnum + } + + // now try weight + cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait); + if (cfnum != NULL) { + CGFloat val; + + // LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default? + if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) + complain("error getting weight value in matchTraits()"); + closeness[i].weight = val - targetWeight; + } else + // okay there's no weight key; let's try the literal meaning of the symbolic constant + // LONGTERM is the weight key guaranteed? + if ((symbolic & kCTFontBoldTrait) != 0) + closeness[i].weight = ourNSFontWeightBold - targetWeight; + else + closeness[i].weight = ourNSFontWeightRegular - targetWeight; + + // italics is a bit harder because Core Text doesn't expose a concept of obliqueness + // Pango just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + if ((symbolic & kCTFontItalicTrait) != 0) + closeness[i].italic = italicCloseness; + else { + CFStringRef styleName; + BOOL isOblique; + + isOblique = NO; // default value + styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + closeness[i].italic = obliqueCloseness; + else + closeness[i].italic = normalCloseness; + } + + // now try width + // TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on + // TODO verify the rest of this matrix (what matrix?) + cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait); + if (cfnum != NULL) { + CGFloat val; + + if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) + complain("error getting width value in matchTraits()"); + closeness[i].stretch = val - targetStretch; + } else + // okay there's no width key; let's try the literal meaning of the symbolic constant + // LONGTERM is the width key guaranteed? + if ((symbolic & kCTFontExpandedTrait) != 0) + closeness[i].stretch = 1.0 - targetStretch; + else if ((symbolic & kCTFontCondensedTrait) != 0) + closeness[i].stretch = -1.0 - targetStretch; + else + closeness[i].stretch = 0.0 - targetStretch; + + CFRelease(traits); + } + + // now figure out the 3-space difference between the three and sort by that + for (i = 0; i < n; i++) { + CGFloat weight, italic, stretch; + + weight = closeness[i].weight; + weight *= weight; + italic = closeness[i].italic; + italic *= italic; + stretch = closeness[i].stretch; + stretch *= stretch; + closeness[i].distance = sqrt(weight + italic + stretch); + } + qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { + const struct closeness *a = (const struct closeness *) aa; + const struct closeness *b = (const struct closeness *) bb; + + // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions + // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? + return (a->distance > b->distance) - (a->distance < b->distance); + }); + // and the first element of the sorted array is what we want + out = CFArrayGetValueAtIndex(matching, closeness[0].index); + CFRetain(out); // get rule + + // release everything + uiFree(closeness); + CFRelease(matching); + // and release the original descriptor since we no longer need it + CFRelease(against); + + return out; +} + +// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. +CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) +{ + CFDictionaryRef dict; + CFMutableDictionaryRef mdict; + + dict = CTFontDescriptorCopyAttributes(desc); + // this might not be mutable, so make a mutable copy + mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); + CFRelease(dict); + return mdict; +} + +uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +{ + CTFontRef f; + CFMutableDictionaryRef attr; + CTFontDescriptorRef cfdesc; + + attr = newAttrList(); + addFontFamilyAttr(attr, desc->Family); + addFontSizeAttr(attr, desc->Size); + + // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back + cfdesc = CTFontDescriptorCreateWithAttributes(attr); + // TODO release attr? + cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); + + // specify the initial size again just to be safe + f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); + // TODO release cfdesc? + + return mkTextFont(f, NO); // we hold the initial reference; no need to retain again +} + +void uiDrawFreeTextFont(uiDrawTextFont *font) +{ + CFRelease(font->f); + uiFree(font); +} + +uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +{ + return (uintptr_t) (font->f); +} + +void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +{ + // TODO +} + +// text sizes and user space points are identical: +// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch +// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch +void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +{ + metrics->Ascent = CTFontGetAscent(font->f); + metrics->Descent = CTFontGetDescent(font->f); + metrics->Leading = CTFontGetLeading(font->f); + metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); + metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); +} + +struct uiDrawTextLayout { + CFMutableAttributedStringRef mas; + CFRange *charsToRanges; + double width; +}; + +uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width) +{ + uiDrawTextLayout *layout; + CFAttributedStringRef immutable; + CFMutableDictionaryRef attr; + CFStringRef backing; + CFIndex i, j, n; + + layout = uiNew(uiDrawTextLayout); + + // TODO docs say we need to use a different set of key callbacks + // TODO see if the font attribute key callbacks need to be the same + attr = newAttrList(); + // this will retain defaultFont->f; no need to worry + CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); + + immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr); + if (immutable == NULL) + complain("error creating immutable attributed string in uiDrawNewTextLayout()"); + CFRelease(attr); + + layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable); + if (layout->mas == NULL) + complain("error creating attributed string in uiDrawNewTextLayout()"); + CFRelease(immutable); + + uiDrawTextLayoutSetWidth(layout, width); + + // unfortunately the CFRanges for attributes expect UTF-16 codepoints + // we want graphemes + // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us + // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway + backing = CFAttributedStringGetString(layout->mas); + n = CFStringGetLength(backing); + // allocate one extra, just to be safe + layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]"); + i = 0; + j = 0; + while (i < n) { + CFRange range; + + range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i); + i = range.location + range.length; + layout->charsToRanges[j] = range; + j++; + } + // and set the last one + layout->charsToRanges[j].location = i; + layout->charsToRanges[j].length = 0; + + return layout; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *layout) +{ + uiFree(layout->charsToRanges); + CFRelease(layout->mas); + uiFree(layout); +} + +void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) +{ + layout->width = width; +} + +struct framesetter { + CTFramesetterRef fs; + CFMutableDictionaryRef frameAttrib; + CGSize extents; +}; + +// TODO CTFrameProgression for RTL/LTR +// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing +static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs) +{ + CFRange fitRange; + CGFloat width; + + fs->fs = CTFramesetterCreateWithAttributedString(layout->mas); + if (fs->fs == NULL) + complain("error creating CTFramesetter object in mkFramesetter()"); + + // TODO kCTFramePathWidthAttributeName? + fs->frameAttrib = NULL; + + width = layout->width; + if (layout->width < 0) + width = CGFLOAT_MAX; + // TODO these seem to be floor()'d or truncated? + fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs, + CFRangeMake(0, 0), + fs->frameAttrib, + CGSizeMake(width, CGFLOAT_MAX), + &fitRange); // not documented as accepting NULL +} + +static void freeFramesetter(struct framesetter *fs) +{ + if (fs->frameAttrib != NULL) + CFRelease(fs->frameAttrib); + CFRelease(fs->fs); +} + +// LONGTERM allow line separation and leading to be factored into a wrapping text layout + +// TODO reconcile differences in character wrapping on platforms +void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +{ + struct framesetter fs; + + mkFramesetter(layout, &fs); + *width = fs.extents.width; + *height = fs.extents.height; + freeFramesetter(&fs); +} + +// Core Text doesn't draw onto a flipped view correctly; we have to do this +// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) +// TODO how is this affected by the CTM? +static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y) +{ + CGContextSaveGState(c); + CGContextTranslateCTM(c, 0, cheight); + CGContextScaleCTM(c, 1.0, -1.0); + CGContextSetTextMatrix(c, CGAffineTransformIdentity); + + // wait, that's not enough; we need to offset y values to account for our new flipping + *y = cheight - *y; +} + +// TODO placement is incorrect for Helvetica +void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout) +{ + struct framesetter fs; + CGRect rect; + CGPathRef path; + CTFrameRef frame; + + prepareContextForText(c, cheight, &y); + mkFramesetter(layout, &fs); + + // oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left + // since we are flipped, we subtract + y -= fs.extents.height; + + rect.origin = CGPointMake(x, y); + rect.size = fs.extents; + path = CGPathCreateWithRect(rect, NULL); + + frame = CTFramesetterCreateFrame(fs.fs, + CFRangeMake(0, 0), + path, + fs.frameAttrib); + if (frame == NULL) + complain("error creating CTFrame object in doDrawText()"); + CTFrameDraw(frame, c); + CFRelease(frame); + + CFRelease(path); + + freeFramesetter(&fs); + CGContextRestoreGState(c); +} + +// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? + +// LONGTERM keep this for later features and documentation purposes +#if 0 + w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL); + // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error + CFRelease(line); + + // LONGTERM provide a way to get the image bounds as a separate function later + bounds = CTLineGetImageBounds(line, c); + // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error + + // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead + CTLineGetTypographicBounds(line, &yoff, NULL, NULL); + // remember that we're flipped, so we subtract + y -= yoff; + CGContextSetTextPosition(c, x, y); +#endif + +static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar) +{ + CFRange start, end; + CFRange out; + + start = layout->charsToRanges[startChar]; + end = layout->charsToRanges[endChar]; + out.location = start.location; + out.length = end.location - start.location; + return out; +} + +#define rangeToCFRange() charsToRange(layout, startChar, endChar) + +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +{ + CGColorSpaceRef colorspace; + CGFloat components[4]; + CGColorRef color; + + // for consistency with windows, use sRGB + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = a; + color = CGColorCreate(colorspace, components); + CGColorSpaceRelease(colorspace); + + CFAttributedStringSetAttribute(layout->mas, + rangeToCFRange(), + kCTForegroundColorAttributeName, + color); + CGColorRelease(color); // TODO safe? +} diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c376536a..64a8233e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -1,655 +1,81 @@ -// 6 september 2015 +// 2 january 2017 #import "uipriv_darwin.h" -// TODO -#define complain(...) implbug(__VA_ARGS__) - -// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) -struct uiDrawFontFamilies { - CFArrayRef fonts; -}; - -uiDrawFontFamilies *uiDrawListFontFamilies(void) -{ - uiDrawFontFamilies *ff; - - ff = uiNew(uiDrawFontFamilies); - ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); - if (ff->fonts == NULL) - implbug("error getting available font names (no reason specified) (TODO)"); - return ff; -} - -int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) -{ - return CFArrayGetCount(ff->fonts); -} - -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) -{ - CFStringRef familystr; - char *family; - - familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); - // toll-free bridge - family = uiDarwinNSStringToText((NSString *) familystr); - // Get Rule means we do not free familystr - return family; -} - -void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) -{ - CFRelease(ff->fonts); - uiFree(ff); -} - -struct uiDrawTextFont { - CTFontRef f; -}; - -uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) -{ - uiDrawTextFont *font; - - font = uiNew(uiDrawTextFont); - font->f = f; - if (retain) - CFRetain(font->f); - return font; -} - -uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) -{ - // toll-free bridging; we do retain, though - return mkTextFont((CTFontRef) f, YES); -} - -static CFMutableDictionaryRef newAttrList(void) -{ - CFMutableDictionaryRef attr; - - attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (attr == NULL) - complain("error creating attribute dictionary in newAttrList()()"); - return attr; -} - -static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) -{ - CFStringRef cfstr; - - cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); - if (cfstr == NULL) - complain("error creating font family name CFStringRef in addFontFamilyAttr()"); - CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); - CFRelease(cfstr); // dictionary holds its own reference -} - -static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) -{ - CFNumberRef n; - - n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); - CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); - CFRelease(n); -} - -#if 0 -TODO -// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do -// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D -static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) -{ - CFMutableArrayRef outerArray; - CFMutableDictionaryRef innerDict; - CFNumberRef numType, numSelector; - int num; - - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) - complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); - - // Apple's headers say these are deprecated, but a few fonts still rely on them - num = kLetterCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray - - // these are the non-deprecated versions of the above; some fonts have these instead - num = kLowerCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kLowerCaseSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray - - CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); - CFRelease(outerArray); -} -#endif - -// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( -// kode54 got these for me before I had access to El Capitan; thanks to him. -#define ourNSFontWeightUltraLight -0.800000 -#define ourNSFontWeightThin -0.600000 -#define ourNSFontWeightLight -0.400000 -#define ourNSFontWeightRegular 0.000000 -#define ourNSFontWeightMedium 0.230000 -#define ourNSFontWeightSemibold 0.300000 -#define ourNSFontWeightBold 0.400000 -#define ourNSFontWeightHeavy 0.560000 -#define ourNSFontWeightBlack 0.620000 -static const CGFloat ctWeights[] = { - // yeah these two have their names swapped; blame Pango - [uiDrawTextWeightThin] = ourNSFontWeightUltraLight, - [uiDrawTextWeightUltraLight] = ourNSFontWeightThin, - [uiDrawTextWeightLight] = ourNSFontWeightLight, - // for this one let's go between Light and Regular - // we're doing nearest so if there happens to be an exact value hopefully it's close enough - [uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2), - [uiDrawTextWeightNormal] = ourNSFontWeightRegular, - [uiDrawTextWeightMedium] = ourNSFontWeightMedium, - [uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold, - [uiDrawTextWeightBold] = ourNSFontWeightBold, - // for this one let's go between Bold and Heavy - [uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), - [uiDrawTextWeightHeavy] = ourNSFontWeightHeavy, - [uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack, -}; - -// Unfortunately there are still no named constants for these. -// Let's just use normalized widths. -// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. -// TODO verify this is correct -static const CGFloat ctStretches[] = { - [uiDrawTextStretchUltraCondensed] = -1.0, - [uiDrawTextStretchExtraCondensed] = -0.75, - [uiDrawTextStretchCondensed] = -0.5, - [uiDrawTextStretchSemiCondensed] = -0.25, - [uiDrawTextStretchNormal] = 0.0, - [uiDrawTextStretchSemiExpanded] = 0.25, - [uiDrawTextStretchExpanded] = 0.5, - [uiDrawTextStretchExtraExpanded] = 0.75, - [uiDrawTextStretchUltraExpanded] = 1.0, -}; - -struct closeness { - CFIndex index; - CGFloat weight; - CGFloat italic; - CGFloat stretch; - CGFloat distance; -}; - -// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. -// We have to implement the closest match ourselves. -// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those. -CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch) -{ - CGFloat targetWeight; - CGFloat italicCloseness, obliqueCloseness, normalCloseness; - CGFloat targetStretch; - CFArrayRef matching; - CFIndex i, n; - struct closeness *closeness; - CTFontDescriptorRef current; - CTFontDescriptorRef out; - - targetWeight = ctWeights[weight]; - switch (italic) { - case uiDrawTextItalicNormal: - italicCloseness = 1; - obliqueCloseness = 1; - normalCloseness = 0; - break; - case uiDrawTextItalicOblique: - italicCloseness = 0.5; - obliqueCloseness = 0; - normalCloseness = 1; - break; - case uiDrawTextItalicItalic: - italicCloseness = 0; - obliqueCloseness = 0.5; - normalCloseness = 1; - break; - } - targetStretch = ctStretches[stretch]; - - matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); - if (matching == NULL) - // no matches; give the original back and hope for the best - return against; - n = CFArrayGetCount(matching); - if (n == 0) { - // likewise - CFRelease(matching); - return against; - } - - closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); - for (i = 0; i < n; i++) { - CFDictionaryRef traits; - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - - closeness[i].index = i; - - current = CFArrayGetValueAtIndex(matching, i); - traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { - // couldn't get traits; be safe by ranking it lowest - // LONGTERM figure out what the longest possible distances are - closeness[i].weight = 3; - closeness[i].italic = 2; - closeness[i].stretch = 3; - continue; - } - - symbolic = 0; // assume no symbolic traits if none are listed - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum != NULL) { - SInt32 s; - - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) - complain("error getting symbolic traits in matchTraits()"); - symbolic = (CTFontSymbolicTraits) s; - // Get rule; do not release cfnum - } - - // now try weight - cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait); - if (cfnum != NULL) { - CGFloat val; - - // LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default? - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting weight value in matchTraits()"); - closeness[i].weight = val - targetWeight; - } else - // okay there's no weight key; let's try the literal meaning of the symbolic constant - // LONGTERM is the weight key guaranteed? - if ((symbolic & kCTFontBoldTrait) != 0) - closeness[i].weight = ourNSFontWeightBold - targetWeight; - else - closeness[i].weight = ourNSFontWeightRegular - targetWeight; - - // italics is a bit harder because Core Text doesn't expose a concept of obliqueness - // Pango just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - if ((symbolic & kCTFontItalicTrait) != 0) - closeness[i].italic = italicCloseness; - else { - CFStringRef styleName; - BOOL isOblique; - - isOblique = NO; // default value - styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - closeness[i].italic = obliqueCloseness; - else - closeness[i].italic = normalCloseness; - } - - // now try width - // TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on - // TODO verify the rest of this matrix (what matrix?) - cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait); - if (cfnum != NULL) { - CGFloat val; - - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting width value in matchTraits()"); - closeness[i].stretch = val - targetStretch; - } else - // okay there's no width key; let's try the literal meaning of the symbolic constant - // LONGTERM is the width key guaranteed? - if ((symbolic & kCTFontExpandedTrait) != 0) - closeness[i].stretch = 1.0 - targetStretch; - else if ((symbolic & kCTFontCondensedTrait) != 0) - closeness[i].stretch = -1.0 - targetStretch; - else - closeness[i].stretch = 0.0 - targetStretch; - - CFRelease(traits); - } - - // now figure out the 3-space difference between the three and sort by that - for (i = 0; i < n; i++) { - CGFloat weight, italic, stretch; - - weight = closeness[i].weight; - weight *= weight; - italic = closeness[i].italic; - italic *= italic; - stretch = closeness[i].stretch; - stretch *= stretch; - closeness[i].distance = sqrt(weight + italic + stretch); - } - qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { - const struct closeness *a = (const struct closeness *) aa; - const struct closeness *b = (const struct closeness *) bb; - - // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions - // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? - return (a->distance > b->distance) - (a->distance < b->distance); - }); - // and the first element of the sorted array is what we want - out = CFArrayGetValueAtIndex(matching, closeness[0].index); - CFRetain(out); // get rule - - // release everything - uiFree(closeness); - CFRelease(matching); - // and release the original descriptor since we no longer need it - CFRelease(against); - - return out; -} - -// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. -CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) -{ - CFDictionaryRef dict; - CFMutableDictionaryRef mdict; - - dict = CTFontDescriptorCopyAttributes(desc); - // this might not be mutable, so make a mutable copy - mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); - CFRelease(dict); - return mdict; -} - -uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) -{ - CTFontRef f; - CFMutableDictionaryRef attr; - CTFontDescriptorRef cfdesc; - - attr = newAttrList(); - addFontFamilyAttr(attr, desc->Family); - addFontSizeAttr(attr, desc->Size); - - // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back - cfdesc = CTFontDescriptorCreateWithAttributes(attr); - // TODO release attr? - cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); - - // specify the initial size again just to be safe - f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); - // TODO release cfdesc? - - return mkTextFont(f, NO); // we hold the initial reference; no need to retain again -} - -void uiDrawFreeTextFont(uiDrawTextFont *font) -{ - CFRelease(font->f); - uiFree(font); -} - -uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) -{ - return (uintptr_t) (font->f); -} - -void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) -{ - // TODO -} - -// text sizes and user space points are identical: -// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch -// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch -void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) -{ - metrics->Ascent = CTFontGetAscent(font->f); - metrics->Descent = CTFontGetDescent(font->f); - metrics->Leading = CTFontGetLeading(font->f); - metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); - metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); -} - struct uiDrawTextLayout { - CFMutableAttributedStringRef mas; - CFRange *charsToRanges; + CFAttributedStringRef attrstr; double width; }; -uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width) +CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { - uiDrawTextLayout *layout; - CFAttributedStringRef immutable; - CFMutableDictionaryRef attr; - CFStringRef backing; - CFIndex i, j, n; + CFStringRef cfstr; + CFAttributedStringRef base; + CFMutableAttributedStringRef mas; + CFMutableDictionaryRef defaultAttrs; - layout = uiNew(uiDrawTextLayout); - - // TODO docs say we need to use a different set of key callbacks - // TODO see if the font attribute key callbacks need to be the same - attr = newAttrList(); - // this will retain defaultFont->f; no need to worry - CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); - - immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr); - if (immutable == NULL) - complain("error creating immutable attributed string in uiDrawNewTextLayout()"); - CFRelease(attr); - - layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable); - if (layout->mas == NULL) - complain("error creating attributed string in uiDrawNewTextLayout()"); - CFRelease(immutable); - - uiDrawTextLayoutSetWidth(layout, width); - - // unfortunately the CFRanges for attributes expect UTF-16 codepoints - // we want graphemes - // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us - // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway - backing = CFAttributedStringGetString(layout->mas); - n = CFStringGetLength(backing); - // allocate one extra, just to be safe - layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]"); - i = 0; - j = 0; - while (i < n) { - CFRange range; - - range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i); - i = range.location + range.length; - layout->charsToRanges[j] = range; - j++; + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); + if (cfstr == NULL) { + // TODO + } + defaultAttrs = CFDictionaryCreateMutable(NULL, 4, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (defaultAttrs == NULL) { + // TODO } - // and set the last one - layout->charsToRanges[j].location = i; - layout->charsToRanges[j].length = 0; - return layout; + base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); + if (base == NULL) { + // TODO + } + CFRelease(cfstr); + CFRelease(defaultAttrs); + mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); + CFRelease(base); + + CFAttributedStringBeginEditing(mas); + // TODO copy in the attributes + CFAttributedStringEndEditing(mas); + + return mas; } -void uiDrawFreeTextLayout(uiDrawTextLayout *layout) +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { - uiFree(layout->charsToRanges); - CFRelease(layout->mas); - uiFree(layout); } -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - layout->width = width; } -struct framesetter { - CTFramesetterRef fs; - CFMutableDictionaryRef frameAttrib; - CGSize extents; -}; - -// TODO CTFrameProgression for RTL/LTR -// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing -static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs) +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - CFRange fitRange; - CGFloat width; - - fs->fs = CTFramesetterCreateWithAttributedString(layout->mas); - if (fs->fs == NULL) - complain("error creating CTFramesetter object in mkFramesetter()"); - - // TODO kCTFramePathWidthAttributeName? - fs->frameAttrib = NULL; - - width = layout->width; - if (layout->width < 0) - width = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs, - CFRangeMake(0, 0), - fs->frameAttrib, - CGSizeMake(width, CGFLOAT_MAX), - &fitRange); // not documented as accepting NULL } -static void freeFramesetter(struct framesetter *fs) +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - if (fs->frameAttrib != NULL) - CFRelease(fs->frameAttrib); - CFRelease(fs->fs); } -// LONGTERM allow line separation and leading to be factored into a wrapping text layout - -// TODO reconcile differences in character wrapping on platforms -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - struct framesetter fs; - - mkFramesetter(layout, &fs); - *width = fs.extents.width; - *height = fs.extents.height; - freeFramesetter(&fs); } -// Core Text doesn't draw onto a flipped view correctly; we have to do this -// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) -// TODO how is this affected by the CTM? -static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y) +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - CGContextSaveGState(c); - CGContextTranslateCTM(c, 0, cheight); - CGContextScaleCTM(c, 1.0, -1.0); - CGContextSetTextMatrix(c, CGAffineTransformIdentity); - - // wait, that's not enough; we need to offset y values to account for our new flipping - *y = cheight - *y; } -// TODO placement is incorrect for Helvetica -void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout) +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - struct framesetter fs; - CGRect rect; - CGPathRef path; - CTFrameRef frame; - - prepareContextForText(c, cheight, &y); - mkFramesetter(layout, &fs); - - // oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left - // since we are flipped, we subtract - y -= fs.extents.height; - - rect.origin = CGPointMake(x, y); - rect.size = fs.extents; - path = CGPathCreateWithRect(rect, NULL); - - frame = CTFramesetterCreateFrame(fs.fs, - CFRangeMake(0, 0), - path, - fs.frameAttrib); - if (frame == NULL) - complain("error creating CTFrame object in doDrawText()"); - CTFrameDraw(frame, c); - CFRelease(frame); - - CFRelease(path); - - freeFramesetter(&fs); - CGContextRestoreGState(c); } -// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? - -// LONGTERM keep this for later features and documentation purposes -#if 0 - w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL); - // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - CFRelease(line); - - // LONGTERM provide a way to get the image bounds as a separate function later - bounds = CTLineGetImageBounds(line, c); - // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error - - // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead - CTLineGetTypographicBounds(line, &yoff, NULL, NULL); - // remember that we're flipped, so we subtract - y -= yoff; - CGContextSetTextPosition(c, x, y); -#endif - -static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar) +void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) { - CFRange start, end; - CFRange out; - - start = layout->charsToRanges[startChar]; - end = layout->charsToRanges[endChar]; - out.location = start.location; - out.length = end.location - start.location; - return out; } -#define rangeToCFRange() charsToRange(layout, startChar, endChar) - -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line) +{ +} + +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { - CGColorSpaceRef colorspace; - CGFloat components[4]; - CGColorRef color; - - // for consistency with windows, use sRGB - colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = a; - color = CGColorCreate(colorspace, components); - CGColorSpaceRelease(colorspace); - - CFAttributedStringSetAttribute(layout->mas, - rangeToCFRange(), - kCTForegroundColorAttributeName, - color); - CGColorRelease(color); // TODO safe? } diff --git a/ui.h b/ui.h index 919f43d3..2a9de749 100644 --- a/ui.h +++ b/ui.h @@ -475,30 +475,11 @@ _UI_EXTERN void uiDrawClip(uiDrawContext *c, uiDrawPath *path); _UI_EXTERN void uiDrawSave(uiDrawContext *c); _UI_EXTERN void uiDrawRestore(uiDrawContext *c); -// TODO manage the use of Text, Font, and TextFont, and of the uiDrawText prefix in general - -///// TODO reconsider this -typedef struct uiDrawFontFamilies uiDrawFontFamilies; - -_UI_EXTERN uiDrawFontFamilies *uiDrawListFontFamilies(void); -_UI_EXTERN int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff); -_UI_EXTERN char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n); -_UI_EXTERN void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff); -///// END TODO - -typedef struct uiDrawTextLayout uiDrawTextLayout; -typedef struct uiDrawTextFont uiDrawTextFont; -typedef struct uiDrawTextFontDescriptor uiDrawTextFontDescriptor; -typedef struct uiDrawTextFontMetrics uiDrawTextFontMetrics; - -struct uiDrawTextFontDescriptor { - const char *Family; - double Size; - uiDrawTextWeight Weight; - uiDrawTextItalic Italic; - uiDrawTextStretch Stretch; -}; +// TODO merge back in +#include "ui_attrstr.h" +// TODO +#if 0 struct uiDrawTextFontMetrics { double Ascent; double Descent; @@ -515,18 +496,7 @@ _UI_EXTERN void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescr // TODO make copy with given attributes methods? // TODO yuck this name _UI_EXTERN void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics); - -// TODO initial line spacing? and what about leading? -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width); -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *layout); -// TODO get width -_UI_EXTERN void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width); -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height); - -// and the attributes that you can set on a text layout -_UI_EXTERN void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a); - -_UI_EXTERN void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout); +#endif _UI_ENUM(uiModifiers) { uiModifierCtrl = 1 << 0, diff --git a/common/ui_attrstr.h b/ui_attrstr.h similarity index 98% rename from common/ui_attrstr.h rename to ui_attrstr.h index 7bfa0af9..274d059d 100644 --- a/common/ui_attrstr.h +++ b/ui_attrstr.h @@ -92,6 +92,7 @@ struct uiDrawTextLayoutLineMetrics { double X; double Y; double Width; + // height = ascent + descent + leading (TODO formally document) double Ascent; double Descent; double Leading; From 4318785eb2c99bdf5ac5cfb461620b9d0e188567 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 3 Jan 2017 12:18:17 -0500 Subject: [PATCH 038/487] More drawtext.m work. I was wrong; I'll need to do the trait matching anyway. Ugh. --- darwin/drawtext.m | 131 ++++++++++++++++++++++++++++++++++++++++++++-- ui_attrstr.h | 1 + 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 64a8233e..f93111c1 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -6,23 +6,146 @@ struct uiDrawTextLayout { double width; }; -CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) +// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights +// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these +// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) +static const double weightsToCTWeights[] = { + -1.0, // 0..99 + -0.7, // 100..199 + -0.5, // 200..299 + -0.23, // 300..399 + 0.0, // 400..499 + 0.2, // 500..599 + 0.3, // 600..699 + 0.4, // 700..799 + 0.6, // 800..899 + 0.8, // 900..999 + 1.0, // 1000 +}; + +static double weightToCTWeight(uiDrawTextWeight weight) +{ + int weightClass; + double ctclass; + double rest, weightFloor, nextFloor; + + if (weight <= 0) + return -1.0; + if (weight >= 1000) + return 1.0; + + weightClass = weight / 100; + rest = (double) weight; + weightFloor = (double) (weightClass * 100); + nextFloor = (double) ((weightClass + 1) * 100); + rest = (rest - weightFloor) / (nextFloor - weightFloor); + + ctclass = weightsToCTWeights[weightClass]; + return fma(rest, + weightsToCTWeights[weightClass + 1] - ctclass, + ctclass); +} + +// TODO put italics here + +// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) +static const double stretchesToCTWidths[] = { + [uiDrawTextStretchUltraCondensed] = -0.400000, + [uiDrawTextStretchExtraCondensed] = -0.300000, + [uiDrawTextStretchCondensed] = -0.200000, + [uiDrawTextStretchSemiCondensed] = -0.100000, + [uiDrawTextStretchNormal] = 0.000000, + [uiDrawTextStretchSemiExpanded] = 0.100000, + [uiDrawTextStretchExpanded] = 0.200000, + [uiDrawTextStretchExtraExpanded] = 0.300000, + // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) + [uiDrawTextStretchUltraExpanded] = 0.400000, +}; + +static CFDictionaryRef fontdescToTraits(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef traits; + CFNumberRef num; + double x; + + traits = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (traits == NULL) { + // TODO + } + + x = weightToCTWeight(fd->Weight); + num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); + CFDictionaryAddValue(traits, kCTFontWeightTrait, num); + CFRelease(num); + + // TODO italics + + x = stretchesToCTWidths[fd->Stretch]; + num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); + CFDictionaryAddValue(traits, kCTFontWidthTrait, num); + CFRelease(num); + + return traits; +} + +static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef attrs; + CFStringRef cffamily; + CFDictionaryRef traits; + CTFontDescriptorRef desc; + CTFontRef font; + + attrs = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (attrs == NULL) { + // TODO + } + cffamily = CFStringCreateWithCString(NULL, fd.Family, kCFStringEncodingUTF8); + if (cffamily == NULL) { + // TODO + } + CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); + CFRelease(cffamily); + traits = fontdescToTraits(fd); + CFDictionaryAddValue(attrs, kCTFontTraitsAttribute, traits); + CFRelease(traits); + + desc = CTFontDescriptorCreateWithAttributes(attrs); + CFRelease(attrs); // TODO correct? + // This function DOES return a font with the closest traits that are available, so we don't have to do any manual matching. + // TODO figure out why we had to for other things... + font = CTFontCreateWithFontDescriptor(desc, fd.Size, NULL); + CFRelease(desc); // TODO correct? + return font; +} + +static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; CFAttributedStringRef base; CFMutableAttributedStringRef mas; - CFMutableDictionaryRef defaultAttrs; cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); if (cfstr == NULL) { // TODO } - defaultAttrs = CFDictionaryCreateMutable(NULL, 4, + defaultAttrs = CFDictionaryCreateMutable(NULL, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (defaultAttrs == NULL) { // TODO } + defaultCTFont = fontdescToCTFont(defaultFont); + CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); + CFRelease(defaultCTFont); base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); if (base == NULL) { @@ -46,6 +169,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + CFRelease(tl->attrstr); + uiFree(tl); } void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) diff --git a/ui_attrstr.h b/ui_attrstr.h index 274d059d..c7e61667 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -78,6 +78,7 @@ _UI_ENUM(uiDrawTextStretch) { struct uiDrawFontDescriptor { char *Family; + // TODO rename to PointSize? double Size; uiDrawTextWeight Weight; uiDrawTextItalic Italic; From bab798543f843beae09b8df829940d4a60f4df90 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 3 Jan 2017 13:42:12 -0500 Subject: [PATCH 039/487] Wrote the new font matching code. This is taken from the old code, but cleaned up considerably and updated with new knowledge. --- darwin/_old_drawtext.m | 214 --------------------------------------- darwin/fontmatch.m | 223 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 214 deletions(-) create mode 100644 darwin/fontmatch.m diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index c376536a..14d0a64b 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -151,220 +151,6 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) #define ourNSFontWeightBold 0.400000 #define ourNSFontWeightHeavy 0.560000 #define ourNSFontWeightBlack 0.620000 -static const CGFloat ctWeights[] = { - // yeah these two have their names swapped; blame Pango - [uiDrawTextWeightThin] = ourNSFontWeightUltraLight, - [uiDrawTextWeightUltraLight] = ourNSFontWeightThin, - [uiDrawTextWeightLight] = ourNSFontWeightLight, - // for this one let's go between Light and Regular - // we're doing nearest so if there happens to be an exact value hopefully it's close enough - [uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2), - [uiDrawTextWeightNormal] = ourNSFontWeightRegular, - [uiDrawTextWeightMedium] = ourNSFontWeightMedium, - [uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold, - [uiDrawTextWeightBold] = ourNSFontWeightBold, - // for this one let's go between Bold and Heavy - [uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), - [uiDrawTextWeightHeavy] = ourNSFontWeightHeavy, - [uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack, -}; - -// Unfortunately there are still no named constants for these. -// Let's just use normalized widths. -// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. -// TODO verify this is correct -static const CGFloat ctStretches[] = { - [uiDrawTextStretchUltraCondensed] = -1.0, - [uiDrawTextStretchExtraCondensed] = -0.75, - [uiDrawTextStretchCondensed] = -0.5, - [uiDrawTextStretchSemiCondensed] = -0.25, - [uiDrawTextStretchNormal] = 0.0, - [uiDrawTextStretchSemiExpanded] = 0.25, - [uiDrawTextStretchExpanded] = 0.5, - [uiDrawTextStretchExtraExpanded] = 0.75, - [uiDrawTextStretchUltraExpanded] = 1.0, -}; - -struct closeness { - CFIndex index; - CGFloat weight; - CGFloat italic; - CGFloat stretch; - CGFloat distance; -}; - -// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. -// We have to implement the closest match ourselves. -// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those. -CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch) -{ - CGFloat targetWeight; - CGFloat italicCloseness, obliqueCloseness, normalCloseness; - CGFloat targetStretch; - CFArrayRef matching; - CFIndex i, n; - struct closeness *closeness; - CTFontDescriptorRef current; - CTFontDescriptorRef out; - - targetWeight = ctWeights[weight]; - switch (italic) { - case uiDrawTextItalicNormal: - italicCloseness = 1; - obliqueCloseness = 1; - normalCloseness = 0; - break; - case uiDrawTextItalicOblique: - italicCloseness = 0.5; - obliqueCloseness = 0; - normalCloseness = 1; - break; - case uiDrawTextItalicItalic: - italicCloseness = 0; - obliqueCloseness = 0.5; - normalCloseness = 1; - break; - } - targetStretch = ctStretches[stretch]; - - matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); - if (matching == NULL) - // no matches; give the original back and hope for the best - return against; - n = CFArrayGetCount(matching); - if (n == 0) { - // likewise - CFRelease(matching); - return against; - } - - closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); - for (i = 0; i < n; i++) { - CFDictionaryRef traits; - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - - closeness[i].index = i; - - current = CFArrayGetValueAtIndex(matching, i); - traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { - // couldn't get traits; be safe by ranking it lowest - // LONGTERM figure out what the longest possible distances are - closeness[i].weight = 3; - closeness[i].italic = 2; - closeness[i].stretch = 3; - continue; - } - - symbolic = 0; // assume no symbolic traits if none are listed - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum != NULL) { - SInt32 s; - - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) - complain("error getting symbolic traits in matchTraits()"); - symbolic = (CTFontSymbolicTraits) s; - // Get rule; do not release cfnum - } - - // now try weight - cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait); - if (cfnum != NULL) { - CGFloat val; - - // LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default? - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting weight value in matchTraits()"); - closeness[i].weight = val - targetWeight; - } else - // okay there's no weight key; let's try the literal meaning of the symbolic constant - // LONGTERM is the weight key guaranteed? - if ((symbolic & kCTFontBoldTrait) != 0) - closeness[i].weight = ourNSFontWeightBold - targetWeight; - else - closeness[i].weight = ourNSFontWeightRegular - targetWeight; - - // italics is a bit harder because Core Text doesn't expose a concept of obliqueness - // Pango just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - if ((symbolic & kCTFontItalicTrait) != 0) - closeness[i].italic = italicCloseness; - else { - CFStringRef styleName; - BOOL isOblique; - - isOblique = NO; // default value - styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - closeness[i].italic = obliqueCloseness; - else - closeness[i].italic = normalCloseness; - } - - // now try width - // TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on - // TODO verify the rest of this matrix (what matrix?) - cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait); - if (cfnum != NULL) { - CGFloat val; - - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting width value in matchTraits()"); - closeness[i].stretch = val - targetStretch; - } else - // okay there's no width key; let's try the literal meaning of the symbolic constant - // LONGTERM is the width key guaranteed? - if ((symbolic & kCTFontExpandedTrait) != 0) - closeness[i].stretch = 1.0 - targetStretch; - else if ((symbolic & kCTFontCondensedTrait) != 0) - closeness[i].stretch = -1.0 - targetStretch; - else - closeness[i].stretch = 0.0 - targetStretch; - - CFRelease(traits); - } - - // now figure out the 3-space difference between the three and sort by that - for (i = 0; i < n; i++) { - CGFloat weight, italic, stretch; - - weight = closeness[i].weight; - weight *= weight; - italic = closeness[i].italic; - italic *= italic; - stretch = closeness[i].stretch; - stretch *= stretch; - closeness[i].distance = sqrt(weight + italic + stretch); - } - qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { - const struct closeness *a = (const struct closeness *) aa; - const struct closeness *b = (const struct closeness *) bb; - - // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions - // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? - return (a->distance > b->distance) - (a->distance < b->distance); - }); - // and the first element of the sorted array is what we want - out = CFArrayGetValueAtIndex(matching, closeness[0].index); - CFRetain(out); // get rule - - // release everything - uiFree(closeness); - CFRelease(matching); - // and release the original descriptor since we no longer need it - CFRelease(against); - - return out; -} // Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m new file mode 100644 index 00000000..c8840d76 --- /dev/null +++ b/darwin/fontmatch.m @@ -0,0 +1,223 @@ +// 3 january 2017 +#import "uipriv_darwin.h" + +// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. +// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for +// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) +// - CTFontCreateWithFontDescriptor() +// - CTFontCreateCopyWithAttributes() +// We have to implement the closest match ourselves. +// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. + +struct closeness { + CFIndex index; + double weight; + double italic; + double stretch; + double distance; +}; + +static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) +{ + CFNumberRef cfnum; + double val; + + cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { + // TODO + } + // Get Rule; do not release cfnum + return val; +} + +struct italicCloseness { + double normal; + double oblique; + double italic; +}; + +// remember that in closeness, 0 means exact +// in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match" +static const struct italicCloseness italicClosenesses[] = { + [uiDrawTextItalicNormal] = { 0, 1, 1 }, + [uiDrawTextItalicOblique] = { 1, 0, 0.5 }, + [uiDrawTextItalicItalic] = { 1, 0.5, 0 }, +}; + +// Italics are hard because Core Text does NOT distinguish between italic and oblique. +// All Core Text provides is a slant value and the italic bit of the symbolic traits mask. +// However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. +// Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) +// TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) +static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +{ + struct italicCloseness *ic = &(italicClosenesses[italic]); + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work + SInt32 s; + CFStringRef styleName; + BOOL isOblique; + + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { + // TODO + } + symbolic = (CTFontSymbolicTraits) s; + // Get Rule; do not release cfnum + if ((symbolic & kCTFontItalicTrait) == 0) + return ic->normal; + + // Okay, now we know it's either Italic or Oblique + // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); + // TODO is styleName guaranteed? + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return ic->oblique; + return ic->italic; +} + +static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +{ + CFArrayRef matching; + CFIndex i, n; + struct closeness *closeness; + CTFontDescriptorRef current; + CTFontDescriptorRef out; + + matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); + if (matching == NULL) + // no matches; give the original back and hope for the best + return against; + n = CFArrayGetCount(matching); + if (n == 0) { + // likewise + CFRelease(matching); + return against; + } + + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + for (i = 0; i < n; i++) { + CFDictionaryRef traits; + + closeness[i].index = i; + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); + if (traits == NULL) { + // couldn't get traits; be safe by ranking it lowest + // LONGTERM figure out what the longest possible distances are + closeness[i].weight = 3; + closeness[i].italic = 2; + closeness[i].stretch = 3; + continue; + } + closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; + closeness[i].italic = italicCloseness(current, traits, targetItalic); + closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; + CFRelease(traits); + } + + // now figure out the 3-space difference between the three and sort by that + // TODO merge this loop with the previous loop? + for (i = 0; i < n; i++) { + double weight, italic, stretch; + + weight = closeness[i].weight; + weight *= weight; + italic = closeness[i].italic; + italic *= italic; + stretch = closeness[i].stretch; + stretch *= stretch; + closeness[i].distance = sqrt(weight + italic + stretch); + } + qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { + const struct closeness *a = (const struct closeness *) aa; + const struct closeness *b = (const struct closeness *) bb; + + // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions + // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? + return (a->distance > b->distance) - (a->distance < b->distance); + }); + // and the first element of the sorted array is what we want + out = CFArrayGetValueAtIndex(matching, closeness[0].index); + CFRetain(out); // get rule + + // release everything + uiFree(closeness); + CFRelease(matching); + // and release the original descriptor since we no longer need it + CFRelease(against); + + return out; +} + +// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights +// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these +// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) +static const double weightsToCTWeights[] = { + -1.0, // 0..99 + -0.7, // 100..199 + -0.5, // 200..299 + -0.23, // 300..399 + 0.0, // 400..499 + 0.2, // 500..599 + 0.3, // 600..699 + 0.4, // 700..799 + 0.6, // 800..899 + 0.8, // 900..999 + 1.0, // 1000 +}; + +static double weightToCTWeight(uiDrawTextWeight weight) +{ + int weightClass; + double ctclass; + double rest, weightFloor, nextFloor; + + if (weight <= 0) + return -1.0; + if (weight >= 1000) + return 1.0; + + weightClass = weight / 100; + rest = (double) weight; + weightFloor = (double) (weightClass * 100); + nextFloor = (double) ((weightClass + 1) * 100); + rest = (rest - weightFloor) / (nextFloor - weightFloor); + + ctclass = weightsToCTWeights[weightClass]; + return fma(rest, + weightsToCTWeights[weightClass + 1] - ctclass, + ctclass); +} + +// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) +static const double stretchesToCTWidths[] = { + [uiDrawTextStretchUltraCondensed] = -0.400000, + [uiDrawTextStretchExtraCondensed] = -0.300000, + [uiDrawTextStretchCondensed] = -0.200000, + [uiDrawTextStretchSemiCondensed] = -0.100000, + [uiDrawTextStretchNormal] = 0.000000, + [uiDrawTextStretchSemiExpanded] = 0.100000, + [uiDrawTextStretchExpanded] = 0.200000, + [uiDrawTextStretchExtraExpanded] = 0.300000, + // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) + [uiDrawTextStretchUltraExpanded] = 0.400000, +}; From f147edf94902bfc3de9e44dd26905e9b7fba19b5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 3 Jan 2017 23:59:23 -0500 Subject: [PATCH 040/487] More work. --- darwin/CMakeLists.txt | 1 + darwin/_old_drawtext.m | 32 --------- darwin/drawtext.m | 151 +++++++++++------------------------------ darwin/fontmatch.m | 32 +++++++++ darwin/uipriv_darwin.h | 8 +-- 5 files changed, 77 insertions(+), 147 deletions(-) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index dc99b864..385a4a53 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND _LIBUI_SOURCES darwin/editablecombo.m darwin/entry.m darwin/fontbutton.m + darwin/fontmatch.m darwin/form.m darwin/graphemes.m darwin/grid.m diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index 14d0a64b..96cf696e 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -292,38 +292,6 @@ struct framesetter { CGSize extents; }; -// TODO CTFrameProgression for RTL/LTR -// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing -static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs) -{ - CFRange fitRange; - CGFloat width; - - fs->fs = CTFramesetterCreateWithAttributedString(layout->mas); - if (fs->fs == NULL) - complain("error creating CTFramesetter object in mkFramesetter()"); - - // TODO kCTFramePathWidthAttributeName? - fs->frameAttrib = NULL; - - width = layout->width; - if (layout->width < 0) - width = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs, - CFRangeMake(0, 0), - fs->frameAttrib, - CGSizeMake(width, CGFLOAT_MAX), - &fitRange); // not documented as accepting NULL -} - -static void freeFramesetter(struct framesetter *fs) -{ - if (fs->frameAttrib != NULL) - CFRelease(fs->frameAttrib); - CFRelease(fs->fs); -} - // LONGTERM allow line separation and leading to be factored into a wrapping text layout // TODO reconcile differences in character wrapping on platforms diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f93111c1..f5e1acdb 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -4,123 +4,18 @@ struct uiDrawTextLayout { CFAttributedStringRef attrstr; double width; + CTFramesetterRef framesetter; + CGSize size; + CGPathRef path; }; -// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights -// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these -// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) -static const double weightsToCTWeights[] = { - -1.0, // 0..99 - -0.7, // 100..199 - -0.5, // 200..299 - -0.23, // 300..399 - 0.0, // 400..499 - 0.2, // 500..599 - 0.3, // 600..699 - 0.4, // 700..799 - 0.6, // 800..899 - 0.8, // 900..999 - 1.0, // 1000 -}; - -static double weightToCTWeight(uiDrawTextWeight weight) -{ - int weightClass; - double ctclass; - double rest, weightFloor, nextFloor; - - if (weight <= 0) - return -1.0; - if (weight >= 1000) - return 1.0; - - weightClass = weight / 100; - rest = (double) weight; - weightFloor = (double) (weightClass * 100); - nextFloor = (double) ((weightClass + 1) * 100); - rest = (rest - weightFloor) / (nextFloor - weightFloor); - - ctclass = weightsToCTWeights[weightClass]; - return fma(rest, - weightsToCTWeights[weightClass + 1] - ctclass, - ctclass); -} - -// TODO put italics here - -// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) -static const double stretchesToCTWidths[] = { - [uiDrawTextStretchUltraCondensed] = -0.400000, - [uiDrawTextStretchExtraCondensed] = -0.300000, - [uiDrawTextStretchCondensed] = -0.200000, - [uiDrawTextStretchSemiCondensed] = -0.100000, - [uiDrawTextStretchNormal] = 0.000000, - [uiDrawTextStretchSemiExpanded] = 0.100000, - [uiDrawTextStretchExpanded] = 0.200000, - [uiDrawTextStretchExtraExpanded] = 0.300000, - // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) - [uiDrawTextStretchUltraExpanded] = 0.400000, -}; - -static CFDictionaryRef fontdescToTraits(uiDrawFontDescriptor *fd) -{ - CFMutableDictionaryRef traits; - CFNumberRef num; - double x; - - traits = CFDictionaryCreateMutable(NULL, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (traits == NULL) { - // TODO - } - - x = weightToCTWeight(fd->Weight); - num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); - CFDictionaryAddValue(traits, kCTFontWeightTrait, num); - CFRelease(num); - - // TODO italics - - x = stretchesToCTWidths[fd->Stretch]; - num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); - CFDictionaryAddValue(traits, kCTFontWidthTrait, num); - CFRelease(num); - - return traits; -} - static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) { - CFMutableDictionaryRef attrs; - CFStringRef cffamily; - CFDictionaryRef traits; CTFontDescriptorRef desc; CTFontRef font; - attrs = CFDictionaryCreateMutable(NULL, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (attrs == NULL) { - // TODO - } - cffamily = CFStringCreateWithCString(NULL, fd.Family, kCFStringEncodingUTF8); - if (cffamily == NULL) { - // TODO - } - CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); - CFRelease(cffamily); - traits = fontdescToTraits(fd); - CFDictionaryAddValue(attrs, kCTFontTraitsAttribute, traits); - CFRelease(traits); - - desc = CTFontDescriptorCreateWithAttributes(attrs); - CFRelease(attrs); // TODO correct? - // This function DOES return a font with the closest traits that are available, so we don't have to do any manual matching. - // TODO figure out why we had to for other things... - font = CTFontCreateWithFontDescriptor(desc, fd.Size, NULL); + desc = fontdescToCTFontDescriptor(fd); + font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); CFRelease(desc); // TODO correct? return font; } @@ -165,10 +60,46 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { + uiDrawTextLayout *tl; + CGFloat cgwidth; + CFRange range, unused; + CGRect rect; + + tl = uiNew(uiDrawTextLayout); + tl->attrstr = attrstrToCoreFoundation(s, defaultFont); + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + tl->width = width; + + // TODO CTFrameProgression for RTL/LTR + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); + if (tl->framesetter == NULL) { + // TODO + } + + cgwidth = (CGFloat) width; + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + // TODO these seem to be floor()'d or truncated? + // TODO double check to make sure this TODO was right + tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL + + rect.origin = CGZeroPoint; + rect.size = tl->size; + tl->path = CGPathCreateWithRect(rect, NULL); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + CFRelease(tl->path); + CFRelease(tl->framesetter); CFRelease(tl->attrstr); uiFree(tl); } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index c8840d76..31e2a4d1 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -221,3 +221,35 @@ static const double stretchesToCTWidths[] = { // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) [uiDrawTextStretchUltraExpanded] = 0.400000, }; + +CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef attrs; + CFStringRef cffamily; + CFNumberRef cfsize; + CTFontDescriptorRef basedesc; + + attrs = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (attrs == NULL) { + // TODO + } + cffamily = CFStringCreateWithCString(NULL, fd->Family, kCFStringEncodingUTF8); + if (cffamily == NULL) { + // TODO + } + CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); + CFRelease(cffamily); + cfsize = CFNumberCreate(NULL, kCFNumberDoubleType, &(fd->Size)); + CFDictionaryAddValue(attrs, kCTFontSizeAttribute, cfsize); + CFRelease(cfsize); + + basedesc = CTFontDescriptorCreateWithAttributes(attrs); + CFRelease(attrs); // TODO correct? + return matchTraits(basedesc, + weightToCTWeight(fd->Weight), + fd->Italic, + stretchesToCTWidths[fd->Stretch]); +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 6bca87b2..125fd94a 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -108,11 +108,6 @@ extern BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod); extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); -// drawtext.m -extern uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain); -extern uiDrawTextFont *mkTextFontFromNSFont(NSFont *f); -extern void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout); - // fontbutton.m extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); @@ -144,3 +139,6 @@ extern NSImage *imageImage(uiImage *); // winmoveresize.m extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); + +// fontmatch.m +extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); From dfaf640101c18a022266033dd137b51fd4b811f0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 4 Jan 2017 23:50:08 -0500 Subject: [PATCH 041/487] More work. Core Text chaos has resurged... --- darwin/_old_drawtext.m | 101 ++--------------------------------------- darwin/drawtext.m | 45 ++++++++++++++++++ ui_attrstr.h | 1 + 3 files changed, 50 insertions(+), 97 deletions(-) diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index 96cf696e..14346784 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -1,9 +1,6 @@ // 6 september 2015 #import "uipriv_darwin.h" -// TODO -#define complain(...) implbug(__VA_ARGS__) - // TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) struct uiDrawFontFamilies { CFArrayRef fonts; @@ -140,6 +137,7 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) } #endif +#if 0 // Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( // kode54 got these for me before I had access to El Capitan; thanks to him. #define ourNSFontWeightUltraLight -0.800000 @@ -151,6 +149,7 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) #define ourNSFontWeightBold 0.400000 #define ourNSFontWeightHeavy 0.560000 #define ourNSFontWeightBlack 0.620000 +#endif // Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) @@ -215,83 +214,6 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); } -struct uiDrawTextLayout { - CFMutableAttributedStringRef mas; - CFRange *charsToRanges; - double width; -}; - -uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width) -{ - uiDrawTextLayout *layout; - CFAttributedStringRef immutable; - CFMutableDictionaryRef attr; - CFStringRef backing; - CFIndex i, j, n; - - layout = uiNew(uiDrawTextLayout); - - // TODO docs say we need to use a different set of key callbacks - // TODO see if the font attribute key callbacks need to be the same - attr = newAttrList(); - // this will retain defaultFont->f; no need to worry - CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); - - immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr); - if (immutable == NULL) - complain("error creating immutable attributed string in uiDrawNewTextLayout()"); - CFRelease(attr); - - layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable); - if (layout->mas == NULL) - complain("error creating attributed string in uiDrawNewTextLayout()"); - CFRelease(immutable); - - uiDrawTextLayoutSetWidth(layout, width); - - // unfortunately the CFRanges for attributes expect UTF-16 codepoints - // we want graphemes - // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us - // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway - backing = CFAttributedStringGetString(layout->mas); - n = CFStringGetLength(backing); - // allocate one extra, just to be safe - layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]"); - i = 0; - j = 0; - while (i < n) { - CFRange range; - - range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i); - i = range.location + range.length; - layout->charsToRanges[j] = range; - j++; - } - // and set the last one - layout->charsToRanges[j].location = i; - layout->charsToRanges[j].length = 0; - - return layout; -} - -void uiDrawFreeTextLayout(uiDrawTextLayout *layout) -{ - uiFree(layout->charsToRanges); - CFRelease(layout->mas); - uiFree(layout); -} - -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) -{ - layout->width = width; -} - -struct framesetter { - CTFramesetterRef fs; - CFMutableDictionaryRef frameAttrib; - CGSize extents; -}; - // LONGTERM allow line separation and leading to be factored into a wrapping text layout // TODO reconcile differences in character wrapping on platforms @@ -357,9 +279,6 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL // LONGTERM keep this for later features and documentation purposes #if 0 - w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL); - // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - CFRelease(line); // LONGTERM provide a way to get the image bounds as a separate function later bounds = CTLineGetImageBounds(line, c); @@ -372,20 +291,7 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL CGContextSetTextPosition(c, x, y); #endif -static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar) -{ - CFRange start, end; - CFRange out; - - start = layout->charsToRanges[startChar]; - end = layout->charsToRanges[endChar]; - out.location = start.location; - out.length = end.location - start.location; - return out; -} - -#define rangeToCFRange() charsToRange(layout, startChar, endChar) - +#if 0 void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) { CGColorSpaceRef colorspace; @@ -407,3 +313,4 @@ void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endCh color); CGColorRelease(color); // TODO safe? } +#endif diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f5e1acdb..1272b1da 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -7,6 +7,8 @@ struct uiDrawTextLayout { CTFramesetterRef framesetter; CGSize size; CGPathRef path; + CTFrameRef frame; + CFArrayRef lines; }; static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) @@ -93,11 +95,24 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto rect.origin = CGZeroPoint; rect.size = tl->size; tl->path = CGPathCreateWithRect(rect, NULL); + tl->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + tl->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (tl->frame == NULL) { + // TODO + } + + tl->lines = CTFrameGetLines(tl->frame); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + // TODO release tl->lines? + CFRelease(tl->frame); CFRelease(tl->path); CFRelease(tl->framesetter); CFRelease(tl->attrstr); @@ -114,14 +129,44 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { + return CFArrayGetCount(tl->lines); } +// TODO release when done? +#define getline(tl, line) ((CTLineRef) CFArrayGetValueAtIndex(tl->lines, line)) + void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { + CTLineRef lr; + CFRange range; + + lr = getline(tl, line); + range = CTLineGetStringRange(lr); + // TODO set start and end } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { + CTLineRef lr; + CFRange range; + CGPoint origin; + CGFloat ascent, descent, leading; + + range.location = line; + range.length = 1; + CTFrameGetLineOrigins(tl->frame, range, &origin); + // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? + m->X = origin.x; + m->Y = origin.y; + // TODO is m->Y the baseline position? + // TODO is m->Y flipped? + + lr = getline(tl, line); + // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error + m->Width = CTLineGetTypographicBounds(lr, ascent, descent, leading); + m->Ascent = ascent; + m->Descent = descent; + m->Leading = leading; } void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) diff --git a/ui_attrstr.h b/ui_attrstr.h index c7e61667..4cf7d761 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -97,6 +97,7 @@ struct uiDrawTextLayoutLineMetrics { double Ascent; double Descent; double Leading; + // TODO trailing whitespace? }; _UI_ENUM(uiDrawTextLayoutHitTestResult) { From 3910ff1a13568fc153a42c68625f8473998876bb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 5 Jan 2017 17:55:05 -0500 Subject: [PATCH 042/487] Resolved Core Text pain by not even thinking about lines in terms of boxes. --- darwin/drawtext.m | 11 +++++++---- ui_attrstr.h | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 1272b1da..8d5c400d 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -119,12 +119,16 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } +// TODO what is y, the top-left corner, bottom-left corner, topmost baseline, or bottommost baseline? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { } void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { + // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? + *width = tl->size.width; + *height = tl->size.height; } int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) @@ -157,13 +161,12 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa CTFrameGetLineOrigins(tl->frame, range, &origin); // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? m->X = origin.x; - m->Y = origin.y; - // TODO is m->Y the baseline position? - // TODO is m->Y flipped? + m->BaselineY = origin.y; + // TODO m->Y is flipped lr = getline(tl, line); // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - m->Width = CTLineGetTypographicBounds(lr, ascent, descent, leading); + m->Width = CTLineGetTypographicBounds(lr, &ascent, &descent, &leading); m->Ascent = ascent; m->Descent = descent; m->Leading = leading; diff --git a/ui_attrstr.h b/ui_attrstr.h index 4cf7d761..1d0c4696 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -90,10 +90,12 @@ typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { + // TODO figure out if this is correct regardless of both alignment and writing direction double X; - double Y; + double BaselineY; double Width; - // height = ascent + descent + leading (TODO formally document) + // top-left Y = baseline Y - ascent + // height = ascent + descent + leading (TODO formally document all this) double Ascent; double Descent; double Leading; From 747a0bbfada53e9a8fb4e115d2edcd52ffa063a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 5 Jan 2017 17:56:47 -0500 Subject: [PATCH 043/487] More TODOs. --- darwin/drawtext.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 8d5c400d..944795c0 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -124,6 +124,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { } +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? From 6212ac7238f00e3365d154a0e59f88fc849233f2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 5 Jan 2017 21:36:07 -0500 Subject: [PATCH 044/487] And integrated the rest of our important tests in. --- darwin/drawtext.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 944795c0..cdf766f5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -5,6 +5,11 @@ struct uiDrawTextLayout { CFAttributedStringRef attrstr; double width; CTFramesetterRef framesetter; + // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) + // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path + // (this I confirmed through experimentation) + // so we can just use tl->size for adjustments + // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) CGSize size; CGPathRef path; CTFrameRef frame; @@ -127,7 +132,6 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? *width = tl->size.width; *height = tl->size.height; } @@ -160,10 +164,9 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa range.location = line; range.length = 1; CTFrameGetLineOrigins(tl->frame, range, &origin); - // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? m->X = origin.x; - m->BaselineY = origin.y; - // TODO m->Y is flipped + // and remember that the frame is flipped + m->BaselineY = tl->size.height - origin.y; lr = getline(tl, line); // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error From 1bd2ca22c234b8e6d56cdffd0e741871a4e08364 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 6 Jan 2017 23:53:23 -0500 Subject: [PATCH 045/487] Some more work on the new Cocoa text drawing code. --- darwin/CMakeLists.txt | 1 + darwin/_old_drawtext.m | 48 ------------------------------------------ darwin/draw.h | 6 ++++++ darwin/draw.m | 6 +----- darwin/drawtext.m | 25 +++++++++++++++++++++- 5 files changed, 32 insertions(+), 54 deletions(-) create mode 100644 darwin/draw.h diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 385a4a53..32d1b45e 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND _LIBUI_SOURCES ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) +// TODO is this correct? list(APPEND _LIBUI_INCLUDEDIRS darwin ) diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index 14346784..72a8d2e6 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -227,54 +227,6 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *he freeFramesetter(&fs); } -// Core Text doesn't draw onto a flipped view correctly; we have to do this -// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) -// TODO how is this affected by the CTM? -static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y) -{ - CGContextSaveGState(c); - CGContextTranslateCTM(c, 0, cheight); - CGContextScaleCTM(c, 1.0, -1.0); - CGContextSetTextMatrix(c, CGAffineTransformIdentity); - - // wait, that's not enough; we need to offset y values to account for our new flipping - *y = cheight - *y; -} - -// TODO placement is incorrect for Helvetica -void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout) -{ - struct framesetter fs; - CGRect rect; - CGPathRef path; - CTFrameRef frame; - - prepareContextForText(c, cheight, &y); - mkFramesetter(layout, &fs); - - // oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left - // since we are flipped, we subtract - y -= fs.extents.height; - - rect.origin = CGPointMake(x, y); - rect.size = fs.extents; - path = CGPathCreateWithRect(rect, NULL); - - frame = CTFramesetterCreateFrame(fs.fs, - CFRangeMake(0, 0), - path, - fs.frameAttrib); - if (frame == NULL) - complain("error creating CTFrame object in doDrawText()"); - CTFrameDraw(frame, c); - CFRelease(frame); - - CFRelease(path); - - freeFramesetter(&fs); - CGContextRestoreGState(c); -} - // LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? // LONGTERM keep this for later features and documentation purposes diff --git a/darwin/draw.h b/darwin/draw.h new file mode 100644 index 00000000..41629809 --- /dev/null +++ b/darwin/draw.h @@ -0,0 +1,6 @@ +// 6 january 2017 + +struct uiDrawContext { + CGContextRef c; + CGFloat height; // needed for text; see below +}; diff --git a/darwin/draw.m b/darwin/draw.m index 262ad3e2..231b4dfd 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -1,5 +1,6 @@ // 6 september 2015 #import "uipriv_darwin.h" +#import "draw.h" struct uiDrawPath { CGMutablePathRef path; @@ -103,11 +104,6 @@ void uiDrawPathEnd(uiDrawPath *p) p->ended = TRUE; } -struct uiDrawContext { - CGContextRef c; - CGFloat height; // needed for text; see below -}; - uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) { uiDrawContext *c; diff --git a/darwin/drawtext.m b/darwin/drawtext.m index cdf766f5..a7a400f7 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -1,5 +1,6 @@ // 2 january 2017 #import "uipriv_darwin.h" +#import "draw.h" struct uiDrawTextLayout { CFAttributedStringRef attrstr; @@ -124,9 +125,31 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } -// TODO what is y, the top-left corner, bottom-left corner, topmost baseline, or bottommost baseline? +// TODO double-check helvetica +// TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { + CGContextSaveGState(c->c); + + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped + // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) + // TODO how is this affected by a non-identity CTM? + CGContextTranslateCTM(c->c, 0, cheight); + CGContextScaleCTM(c->c, 1.0, -1.0); + CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); + + // wait, that's not enough; we need to offset y values to account for our new flipping + y = c->height - y; + + // CTFrameDraw() draws in the path we specified when creating the frame + // this means that in our usage, CTFrameDraw() will draw at (0,0) + // so move the origin to be at (x,y) instead + // TODO are the signs correct? + CGContextTranslateCTM(c->c, x, y); + + CTFrameDraw(tl->frame, c->c); + + CGContextRestoreGState(c->c); } // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral From e32341b24bf4874be2d8bfc9664fbd13bf96fcf5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 7 Jan 2017 20:09:44 -0500 Subject: [PATCH 046/487] More work. --- common/attrstr.c | 17 ++++++++ common/uipriv.h | 4 ++ darwin/drawtext.m | 99 ++++++++++++++++++++++++++++++++++++++++++++++- ui_attrstr.h | 24 +++++++++--- 4 files changed, 137 insertions(+), 7 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 102d56eb..76a4214b 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -312,3 +312,20 @@ size_t attrstrUTF16LEn(uiAttributedString *s) { return s->u16len; } + +size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n) +{ + return s->u8tou16[n]; +} + +size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n) +{ + size_t *out; + size_t nbytes; + + nbytes = (s->u16len + 1) * sizeof (size_t); + *n = s->u16len; + out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + memmove(out, s->u16tou8, nbytes); + return out; +} diff --git a/common/uipriv.h b/common/uipriv.h index 31d941ff..f979e4f6 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -64,9 +64,13 @@ struct graphemes { extern int graphemesTakesUTF16(void); extern struct graphemes *graphemes(void *s, size_t len); +// TODO split these into a separate header file? + // attrstr.c extern const uint16_t *attrstrUTF16(uiAttributedString *s); extern size_t attrstrUTF16LEn(uiAttributedString *s); +extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); // attrlist.c struct attrlist; diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a7a400f7..45e24470 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -15,6 +15,8 @@ struct uiDrawTextLayout { CGPathRef path; CTFrameRef frame; CFArrayRef lines; + size_t *u16tou8; + size_t nu16tou8; // TODO I don't like the casing of this name }; static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) @@ -112,11 +114,15 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl->lines = CTFrameGetLines(tl->frame); + // and finally copy the UTF-16 to UTF-8 index conversion table + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + uiFree(tl->u16tou8); // TODO release tl->lines? CFRelease(tl->frame); CFRelease(tl->path); @@ -174,7 +180,8 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start lr = getline(tl, line); range = CTLineGetStringRange(lr); - // TODO set start and end + *start = tl->u16tou8[range.location]; + *end = tl->u16tou8[range.location + range.length]; } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) @@ -203,8 +210,96 @@ void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, i { } -uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line) +static CGPoint *mkLineOrigins(uiDrawTextLayout *tl) { + CGPoint *origins; + CFRange range; + CFIndex i, n; + CTLine line; + CGFloat ascent; + + n = CFArrayGetCount(tl->lines); + range.location = 0; + range.length = n; + origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[]"); + CTFrameGetLineOrigins(tl->frame, range, origins); + for (i = 0; i < n; i++) { + line = getline(tl, i); + CTLineGetTypographicBounds(line, &ascent, NULL, NULL); + origins[i].y = tl->size.height - (origins[i].y + ascent); + } + return origins; +} + +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) +{ + CGPoint *mkLineOrigins; + CFIndex i, n; + CTLineRef line; + double firstYForLine; + CGFloat width, NULL, descent, leading; + CFRange range; + + n = CFArrayGetCount(tl->lines); + if (n == 0) { + // TODO fill result + return; + } + + origins = mkLineOrigins(tl); + if (y < 0) { + line = getline(tl, 0); + width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); + i = 0; + } else { + firstYForLine = 0; + for (i = 0; i < n; i++) { + line = getline(tl, i); + width = CTLineGetTypographicBounds(line, NULL, &descent, &leading); + if (y < maxYForLine) + break; + firstYForLine = origins[i].y + descent + leading; + } + } + if (i == n) { + i--; + result->Line = i; + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + } else { + result->Line = i; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (i == 0 && y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + } + result->InTrailingWhitespace = 0; + range = CTLineGetStringRange(line); + if (x < 0) { + result->Start = tl->u16tou8[range.location]; + result->End = result->Start; + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + } else if (x > tl->size.width) { + result->Start = tl->u16tou8[range.location + range.length]; + result->End = result->Start; + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + } else { + CGPoint pos; + CFIndex index; + + result->XPosition = uiDrawTextLa +youtHitTestPositionInside; + pos.x = x; + // TODO this isn't set properly in any of the fast-track cases + pos.y = y - firstYForLine; + index = CTLineGetStringIn +dexForPosition(line, pos); + if (index == kCFNotFound) { + // TODO + } + result->Pos = tl->u16tou8[index]; + // TODO compute the fractional offset + result->InTrailingWhitespace = x < origins[i].x || x >= (origins[i].x + width); + } + uiFree(origins); } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/ui_attrstr.h b/ui_attrstr.h index 1d0c4696..a9c6ccab 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -87,6 +87,7 @@ struct uiDrawFontDescriptor { typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +typedef struct uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTestResult; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { @@ -102,10 +103,19 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -_UI_ENUM(uiDrawTextLayoutHitTestResult) { - uiDrawTextLayoutHitTestResultNowhere, - uiDrawTextLayoutHitTestResultOnLineTrailingWhitespace, - uiDrawTextLayoutHitTestResultOnCharacter, +_UI_ENUM(uiDrawTextLayoutHitTestPosition) { + uiDrawTextLayoutHitTestPositionBefore, + uiDrawTextLayoutHitTestPositionInside, + uiDrawTextLayoutHitTestPositionAfter, +}; + +struct uiDrawTextLayoutHitTestResult { + size_t Pos; + int Line; + uiDrawTextLayoutHitTestPosition XPosition; + uiDrawTextLayoutHitTestPosition YPosition; + int InTrailingWhitespace; + double XFraction; }; struct uiDrawTextLayoutByteRangeRectangle { @@ -122,6 +132,10 @@ struct uiDrawTextLayoutByteRangeRectangle { // - allow creating a layout out of a substring // - allow marking compositon strings // - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) _UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); @@ -132,7 +146,7 @@ _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, u // TODO redo this? remove it entirely? _UI_EXTERN void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height); // TODO partial offset? -_UI_EXTERN uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line); +_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); // TODO draw only a line? // TODO other layout-specific attributes (alignment, wrapping, etc.)? From e63a42a2904f1382a89ce77673f464826b1d8a2b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 02:17:12 -0500 Subject: [PATCH 047/487] Okay, now that I know what I'm doing, let's make the line metrics struct useful again. --- ui_attrstr.h | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index a9c6ccab..923e3857 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -91,15 +91,33 @@ typedef struct uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTestResult; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { - // TODO figure out if this is correct regardless of both alignment and writing direction + // This describes the overall bounding box of the line. + // TODO figure out if X is correct regardless of both alignment and writing direction double X; - double BaselineY; + double Y; double Width; - // top-left Y = baseline Y - ascent - // height = ascent + descent + leading (TODO formally document all this) + double Height; + + // This describes the typographic bounds of the line. + double BaselineY; double Ascent; double Descent; double Leading; + + // This describes any additional whitespace. + // TODO come up with better names for these. + double ParagraphSpacingBefore; + double LineHeightSpace; + double LineSpacing; + double ParagraphSpacing; + + // Height should equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. + // The above values are listed in vertical order, from top to bottom. + // Ascent + Descent + Leading will give you the typographic bounds of the text. + // BaselineY will be the boundary between Ascent and Descent. + // X, Y, and BaselineY are all in the layout's coordinate system, so the start point of the baseline will be at (X, BaselineY). + // All values will be nonnegative. + // TODO trailing whitespace? }; From 794d30154cb8bd4a819d8e4ef822ffcaa4261a42 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 12:02:42 -0500 Subject: [PATCH 048/487] And rewrote drawtext.m based around the new Core Text research. --- darwin/drawtext.m | 234 +++++++++++++++++++++++++--------------------- ui_attrstr.h | 6 +- 2 files changed, 133 insertions(+), 107 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 45e24470..6ddeedb9 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -2,19 +2,33 @@ #import "uipriv_darwin.h" #import "draw.h" +// TODO what happens if nLines == 0 in any function? + struct uiDrawTextLayout { CFAttributedStringRef attrstr; + + // the width as passed into uiDrawTextLayout constructors double width; + CTFramesetterRef framesetter; + + // the *actual* size of the frame // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path // (this I confirmed through experimentation) // so we can just use tl->size for adjustments // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) CGSize size; + CGPathRef path; CTFrameRef frame; + CFArrayRef lines; + CFIndex nLines; + // we compute this once when first creating the layout + uiDrawTextLayoutLineMetrics *lineMetrics; + + // for converting CFAttributedString indices to byte offsets size_t *u16tou8; size_t nu16tou8; // TODO I don't like the casing of this name }; @@ -68,6 +82,77 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } +static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrame frame, CGSize size) +{ + uiDrawTextLayoutLineMetrics *metrics; + CFArray lines; + CTLineRef line; + CFIndex i, n; + CGFloat ypos; + CGRect bounds, boundsNoLeading; + CGFloat ascent, descent, leading; + CGPoint *origins; + + lines = CTFrameGetLines(frame); + n = CFArrayGetCount(lines); + metrics = (uiDrawTextLay +outLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + + origins = (CGFloat *) uiAlloc(n * sizeof (CGFloat), "CGFloat[] (text layout)"); + CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); + + ypos = size.height; + for (i = 0; i < n; i++) { + line = (CTLineRef) CFArrayGetValueAtIndex(lines, i); + bounds = CTLineGetBoundsWithOptions(line, 0); + boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); + + // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) + ascent = bounds.size.height + bounds.origin.y; + descent = -boundsNoLeading.origin.y; + // TODO does this preserve leading sign? + leading = -bounds.origin.y - descent; + + // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) + ascent = floor(ascent + 0.5); + descent = floor(descent + 0.5); + if (leading > 0) + leading = floor(leading + 0.5); + + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; + metrics[i].Height = ascent + descent + leading; + + metrics[i].BaselineY = origins[i].y; + metrics[i].Ascent = ascent; + metrics[i].Descent = descent; + metrics[i].Leading = leading; + + // TODO + metrics[i].ParagraphSpacingBefore = 0; + metrics[i].LineHeightSpace = 0; + metrics[i].LineSpacing = 0; + metrics[i].ParagraphSpacing = 0; + + // and finally advance to the next line + ypos += metrics[i].Height; + } + + // okay, but now all these metrics are unflipped + // we need to flip them + for (i = 0; i < n; i++) { + metrics[i].Y = size.height - metrics[i].Y; + // go from bottom-left corner to top-left + metrics[i].Y -= metrics[i].Height; + metrics[i].BaselineY = size.height - metrics[i].BaselineY; + // TODO also adjust by metrics[i].Height? + } + + uiFree(origins); + return metrics; +} + uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { uiDrawTextLayout *tl; @@ -113,6 +198,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto } tl->lines = CTFrameGetLines(tl->frame); + tl->nLines = CFArrayGetCount(tl->lines); + tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); // and finally copy the UTF-16 to UTF-8 index conversion table tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); @@ -123,6 +210,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); + uiFree(tl->lineMetrics); // TODO release tl->lines? CFRelease(tl->frame); CFRelease(tl->path); @@ -131,7 +219,6 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } -// TODO double-check helvetica // TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { @@ -167,18 +254,15 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - return CFArrayGetCount(tl->lines); + return tl->nLines; } -// TODO release when done? -#define getline(tl, line) ((CTLineRef) CFArrayGetValueAtIndex(tl->lines, line)) - void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { CTLineRef lr; CFRange range; - lr = getline(tl, line); + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); range = CTLineGetStringRange(lr); *start = tl->u16tou8[range.location]; *end = tl->u16tou8[range.location + range.length]; @@ -186,120 +270,60 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - CTLineRef lr; - CFRange range; - CGPoint origin; - CGFloat ascent, descent, leading; - - range.location = line; - range.length = 1; - CTFrameGetLineOrigins(tl->frame, range, &origin); - m->X = origin.x; - // and remember that the frame is flipped - m->BaselineY = tl->size.height - origin.y; - - lr = getline(tl, line); - // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - m->Width = CTLineGetTypographicBounds(lr, &ascent, &descent, &leading); - m->Ascent = ascent; - m->Descent = descent; - m->Leading = leading; + *m = tl->lineMetrics[line]; } void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) { } -static CGPoint *mkLineOrigins(uiDrawTextLayout *tl) -{ - CGPoint *origins; - CFRange range; - CFIndex i, n; - CTLine line; - CGFloat ascent; - - n = CFArrayGetCount(tl->lines); - range.location = 0; - range.length = n; - origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[]"); - CTFrameGetLineOrigins(tl->frame, range, origins); - for (i = 0; i < n; i++) { - line = getline(tl, i); - CTLineGetTypographicBounds(line, &ascent, NULL, NULL); - origins[i].y = tl->size.height - (origins[i].y + ascent); - } - return origins; -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { - CGPoint *mkLineOrigins; - CFIndex i, n; - CTLineRef line; - double firstYForLine; - CGFloat width, NULL, descent, leading; - CFRange range; + CFIndex i; + CTLine line; + CFIndex pos; + CGFloat charLeft, charRight; - n = CFArrayGetCount(tl->lines); - if (n == 0) { - // TODO fill result - return; - } + if (y >= 0) { + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; - origins = mkLineOrigins(tl); - if (y < 0) { - line = getline(tl, 0); - width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); - i = 0; - } else { - firstYForLine = 0; - for (i = 0; i < n; i++) { - line = getline(tl, i); - width = CTLineGetTypographicBounds(line, NULL, &descent, &leading); - if (y < maxYForLine) + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + if (y >= ltop && y < lbottom) break; - firstYForLine = origins[i].y + descent + leading; } - } - if (i == n) { - i--; - result->Line = i; - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - } else { - result->Line = i; result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (i == 0 && y < 0) - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - } - result->InTrailingWhitespace = 0; - range = CTLineGetStringRange(line); - if (x < 0) { - result->Start = tl->u16tou8[range.location]; - result->End = result->Start; - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - } else if (x > tl->size.width) { - result->Start = tl->u16tou8[range.location + range.length]; - result->End = result->Start; - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - } else { - CGPoint pos; - CFIndex index; - - result->XPosition = uiDrawTextLa -youtHitTestPositionInside; - pos.x = x; - // TODO this isn't set properly in any of the fast-track cases - pos.y = y - firstYForLine; - index = CTLineGetStringIn -dexForPosition(line, pos); - if (index == kCFNotFound) { - // TODO + if (i == tl->nLines) { + i--; + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; } - result->Pos = tl->u16tou8[index]; - // TODO compute the fractional offset - result->InTrailingWhitespace = x < origins[i].x || x >= (origins[i].x + width); + } else { + i = 0; + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; } - uiFree(origins); + m->Line = i; + + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < tl->lineMetrics[i].X) { + result->XPosition = uiDrawTextLay +outHitTestPositionBefore; + // and forcibly return the first character + x = tl->lineMetrics[i].X; + } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { + result->XPosition = uiDrawTextLayoutHitTestP +ositionAfter; + // and forcibly return the last character + x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; + } + + line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + pos = CTLineGetStringIndexForPosition(line, CGP +ointMake(x, 0)); + if (pos == kCFNotFound) { + // TODO + } + m->Pos = tl->u16tou8[pos]; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/ui_attrstr.h b/ui_attrstr.h index 923e3857..7cfedb10 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -132,8 +132,10 @@ struct uiDrawTextLayoutHitTestResult { int Line; uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; - int InTrailingWhitespace; - double XFraction; +// TODO? +// int InTrailingWhitespace; +// TODO? +// double XFraction; }; struct uiDrawTextLayoutByteRangeRectangle { From 7bda3baee3e06056eae59801baf1eace6a06b08f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 13:05:40 -0500 Subject: [PATCH 049/487] Fixed build errors. DOES IT WORK?!?!?!?!?!?!?!?! --- common/attrlist.c | 14 ++++++++------ common/attrstr.c | 12 ++++++------ common/uipriv.h | 7 ++++--- darwin/CMakeLists.txt | 4 ++-- darwin/draw.m | 5 ----- darwin/drawtext.m | 32 ++++++++++++++------------------ darwin/fontmatch.m | 4 ++-- darwin/graphemes.m | 2 +- darwin/uipriv_darwin.h | 6 ++++++ ui.h | 2 ++ 10 files changed, 45 insertions(+), 43 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index bae1203b..f65120f7 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -168,7 +168,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t // we are dropping the left half, so set a->start and unlink a->start = end; *tail = a; - return attrUnlink(attr, a); + return attrUnlink(alist, a); } if (a->end == end) { // chop off the end // we are dropping the right half, so just set a->end @@ -321,11 +321,11 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t // TODO will this cause problems with fonts? // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? if (before->val == val) { - attrGrow(alist, a, start, end); + attrGrow(alist, before, start, end); return; } // okay the values are different; we need to split apart - before = attrDropRange(alist, a, start, end, &tail); + before = attrDropRange(alist, before, start, end, &tail); split = 1; continue; @@ -486,9 +486,10 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above struct attr *tailsAt = NULL; - a = alist->start; + a = alist->first; while (a != NULL) { size_t lstart, lend; + struct attr *tail; // this defines where to re-attach the tails // (all the tails will have their start at end, so we can just insert them all before tailsAt) @@ -532,9 +533,10 @@ void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above struct attr *tailsAt = NULL; - a = alist->start; + a = alist->first; while (a != NULL) { size_t lstart, lend; + struct attr *tail; // this defines where to re-attach the tails // (all the tails will have their start at end, so we can just insert them all before tailsAt) @@ -578,7 +580,7 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) a = attrDeleteRange(alist, a, start, end); } -void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; diff --git a/common/attrstr.c b/common/attrstr.c index 76a4214b..891453cd 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -120,7 +120,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s { uint32_t rune; char buf[4]; - uint16_t u16buf[2]; + uint16_t buf16[2]; size_t n8, n16; size_t old, old16; size_t oldlen, old16len; @@ -238,20 +238,20 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) memmove( s->s + start, s->s + end, - (oldlen - end) * sizeof (char)); + (s->len - end) * sizeof (char)); memmove( s->u16 + start16, s->u16 + end16, - (old16len - end16) * sizeof (uint16_t)); + (s->u16len - end16) * sizeof (uint16_t)); // note the + 1 for these; we want to copy the terminating null too memmove( s->u8tou16 + start, s->u8tou16 + end, - (oldlen - end + 1) * sizeof (size_t)); + (s->len - end + 1) * sizeof (size_t)); memmove( s->u16tou8 + start16, s->u16tou8 + end16, - (old16len - end16 + 1) * sizeof (size_t)); + (s->u16len - end16 + 1) * sizeof (size_t)); // update the conversion tables // note the use of <= to include the null terminator @@ -308,7 +308,7 @@ const uint16_t *attrstrUTF16(uiAttributedString *s) return s->u16; } -size_t attrstrUTF16LEn(uiAttributedString *s) +size_t attrstrUTF16Len(uiAttributedString *s) { return s->u16len; } diff --git a/common/uipriv.h b/common/uipriv.h index f979e4f6..5ed23bf5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -5,6 +5,7 @@ extern "C" { #endif #include +#include #include "controlsigs.h" #include "utf.h" @@ -68,7 +69,7 @@ extern struct graphemes *graphemes(void *s, size_t len); // attrstr.c extern const uint16_t *attrstrUTF16(uiAttributedString *s); -extern size_t attrstrUTF16LEn(uiAttributedString *s); +extern size_t attrstrUTF16Len(uiAttributedString *s); extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); @@ -79,8 +80,8 @@ extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); -extern void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); +extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // TODO move these to the top like everythng else extern struct attrlist *attrlistNew(void); extern void attrlistFree(struct attrlist *alist); diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 32d1b45e..34368c06 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -17,7 +17,7 @@ list(APPEND _LIBUI_SOURCES darwin/drawtext.m darwin/editablecombo.m darwin/entry.m - darwin/fontbutton.m +#TODO darwin/fontbutton.m darwin/fontmatch.m darwin/form.m darwin/graphemes.m @@ -44,7 +44,7 @@ list(APPEND _LIBUI_SOURCES ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) -// TODO is this correct? +# TODO is this correct? list(APPEND _LIBUI_INCLUDEDIRS darwin ) diff --git a/darwin/draw.m b/darwin/draw.m index 231b4dfd..d9aadede 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -443,8 +443,3 @@ void uiDrawRestore(uiDrawContext *c) { CGContextRestoreGState(c->c); } - -void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) -{ - doDrawText(c->c, c->height, x, y, layout); -} diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 6ddeedb9..4296d35a 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -82,10 +82,10 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } -static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrame frame, CGSize size) +static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; - CFArray lines; + CFArrayRef lines; CTLineRef line; CFIndex i, n; CGFloat ypos; @@ -95,10 +95,9 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrame frame, CGSize siz lines = CTFrameGetLines(frame); n = CFArrayGetCount(lines); - metrics = (uiDrawTextLay -outLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); - origins = (CGFloat *) uiAlloc(n * sizeof (CGFloat), "CGFloat[] (text layout)"); + origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)"); CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); ypos = size.height; @@ -183,9 +182,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO kCTFramePathWidthAttributeName? NULL, CGSizeMake(cgwidth, CGFLOAT_MAX), - &unused); // not documented as accepting NULL + &unused); // not documented as accepting NULL (TODO really?) - rect.origin = CGZeroPoint; + rect.origin = CGPointZero; rect.size = tl->size; tl->path = CGPathCreateWithRect(rect, NULL); tl->frame = CTFramesetterCreateFrame(tl->framesetter, @@ -227,7 +226,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) // TODO how is this affected by a non-identity CTM? - CGContextTranslateCTM(c->c, 0, cheight); + CGContextTranslateCTM(c->c, 0, c->height); CGContextScaleCTM(c->c, 1.0, -1.0); CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); @@ -280,9 +279,8 @@ void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, i void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { CFIndex i; - CTLine line; + CTLineRef line; CFIndex pos; - CGFloat charLeft, charRight; if (y >= 0) { for (i = 0; i < tl->nLines; i++) { @@ -302,28 +300,26 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex i = 0; result->YPosition = uiDrawTextLayoutHitTestPositionBefore; } - m->Line = i; + result->Line = i; result->XPosition = uiDrawTextLayoutHitTestPositionInside; if (x < tl->lineMetrics[i].X) { - result->XPosition = uiDrawTextLay -outHitTestPositionBefore; + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; // and forcibly return the first character x = tl->lineMetrics[i].X; } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { - result->XPosition = uiDrawTextLayoutHitTestP -ositionAfter; + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; // and forcibly return the last character x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; } line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - pos = CTLineGetStringIndexForPosition(line, CGP -ointMake(x, 0)); + // TODO copy the part from the docs about this point + pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); if (pos == kCFNotFound) { // TODO } - m->Pos = tl->u16tou8[pos]; + result->Pos = tl->u16tou8[pos]; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 31e2a4d1..04761ef4 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -54,7 +54,7 @@ static const struct italicCloseness italicClosenesses[] = { // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) { - struct italicCloseness *ic = &(italicClosenesses[italic]); + const struct italicCloseness *ic = &(italicClosenesses[italic]); CFNumberRef cfnum; CTFontSymbolicTraits symbolic; // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work @@ -77,7 +77,7 @@ static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, // Okay, now we know it's either Italic or Oblique // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); // TODO is styleName guaranteed? if (styleName != NULL) { CFRange range; diff --git a/darwin/graphemes.m b/darwin/graphemes.m index 3819f0b4..e5ee816f 100644 --- a/darwin/graphemes.m +++ b/darwin/graphemes.m @@ -13,7 +13,7 @@ struct graphemes *graphemes(void *s, size_t len) { struct graphemes *g; UniChar *str = (UniChar *) s; - CFString cfstr; + CFStringRef cfstr; size_t ppos, gpos; CFRange range; size_t i; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 125fd94a..c1fde75a 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -109,9 +109,15 @@ extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); // fontbutton.m +#if 0 /* TODO */ extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); extern void setupFontPanel(void); +#else +static inline BOOL fontButtonInhibitSendAction(SEL sel, id from, id to) { return NO; } +static inline BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) { return NO; } +static inline void setupFontPanel(void) {} +#endif // colorbutton.m extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); diff --git a/ui.h b/ui.h index 2a9de749..ddc9bb63 100644 --- a/ui.h +++ b/ui.h @@ -577,6 +577,7 @@ struct uiAreaKeyEvent { int Up; }; +#if 0 /* TODO */ typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) // TODO document this returns a new font @@ -584,6 +585,7 @@ _UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); _UI_EXTERN uiFontButton *uiNewFontButton(void); +#endif typedef struct uiColorButton uiColorButton; #define uiColorButton(this) ((uiColorButton *) (this)) From 907d7c58300705cc12b1c7b93796053573b52c1b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 13:30:00 -0500 Subject: [PATCH 050/487] Added the beginning of a text-drawing example (since I would need to heavily change the tester to test these things; hopefully in the future the example will be much more sophisticated). Time to fix segfaults! --- examples/CMakeLists.txt | 8 ++- examples/drawtext/main.c | 128 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 examples/drawtext/main.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3a9ec4c9..75b7f9d1 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,8 +31,14 @@ if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() +_add_example(drawtext + drawtext/main.c + ${_EXAMPLE_RESOURCES_RC} +) + add_custom_target(examples DEPENDS controlgallery histogram - cpp-multithread) + cpp-multithread + drawtext) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c new file mode 100644 index 00000000..90c401ee --- /dev/null +++ b/examples/drawtext/main.c @@ -0,0 +1,128 @@ +// 17 january 2017 +#include +#include +#include "../../ui.h" + +uiWindow *mainwin; +uiArea *area; +uiAreaHandler handler; + +const char *text = + "It is with a kind of fear that I begin to write the history of my life. " + "I have, as it were, a superstitious hesitation in lifting the veil that " + "clings about my childhood like a golden mist. The task of writing an " + "autobiography is a difficult one. When I try to classify my earliest " + "impressions, I find that fact and fancy look alike across the years that " + "link the past with the present. The woman paints the child's experiences " + "in her own fantasy. A few impressions stand out vividly from the first " + "years of my life; but \"the shadows of the prison-house are on the rest.\" " + "Besides, many of the joys and sorrows of childhood have lost their " + "poignancy; and many incidents of vital importance in my early education " + "have been forgotten in the excitement of great discoveries. In order, " + "therefore, not to be tedious I shall try to present in a series of " + "sketches only the episodes that seem to me to be the most interesting " + "and important." + ""; +char fontFamily[] = "Palatino"; +// TODO should be const; look at constructor function +uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 18, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +uiAttributedString *attrstr; + +#define margins 5 + +static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + p->AreaWidth - 2 * margins); + uiDrawText(p->Context, layout, margins, margins); + uiDrawFreeTextLayout(layout); +} + +static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) +{ + // do nothing +} + +static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) +{ + // do nothing +} + +static void handlerDragBroken(uiAreaHandler *ah, uiArea *a) +{ + // do nothing +} + +static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) +{ + // reject all keys + return 0; +} + +static int onClosing(uiWindow *w, void *data) +{ + uiControlDestroy(uiControl(mainwin)); + uiQuit(); + return 0; +} + +static int shouldQuit(void *data) +{ + uiControlDestroy(uiControl(mainwin)); + return 1; +} + +int main(void) +{ + uiInitOptions o; + const char *err; + + handler.Draw = handlerDraw; + handler.MouseEvent = handlerMouseEvent; + handler.MouseCrossed = handlerMouseCrossed; + handler.DragBroken = handlerDragBroken; + handler.KeyEvent = handlerKeyEvent; + + memset(&o, 0, sizeof (uiInitOptions)); + err = uiInit(&o); + if (err != NULL) { + fprintf(stderr, "error initializing ui: %s\n", err); + uiFreeInitError(err); + return 1; + } + + uiOnShouldQuit(shouldQuit, NULL); + + attrstr = uiNewAttributedString(text); + + mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); + uiWindowSetMargined(mainwin, 1); + uiWindowOnClosing(mainwin, onClosing, NULL); + + area = uiNewArea(&handler); + uiWindowSetChild(mainwin, uiControl(area)); + + uiControlShow(uiControl(mainwin)); + uiMain(); + uiFreeAttributedString(attrstr); + uiUninit(); + return 0; +} From 7f270942a7e2988401a831bc01a94a2bd0a27b2a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 13:54:23 -0500 Subject: [PATCH 051/487] And fixed errors. Woo, it works!!!!! --- common/attrstr.c | 4 +++- darwin/drawtext.m | 3 ++- examples/drawtext/main.c | 5 ++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 891453cd..bfe42842 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -131,7 +131,9 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // TODO } - at16 = s->u8tou16[at]; + at16 = 0; + if (s->u8tou16 != NULL) + at16 = s->u8tou16[at]; // do this first to reclaim memory invalidateGraphemes(s); diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 4296d35a..a8a34542 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -231,7 +231,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); // wait, that's not enough; we need to offset y values to account for our new flipping - y = c->height - y; + // TODO explain this calculation + y = c->height - tl->size.height - y; // CTFrameDraw() draws in the path we specified when creating the frame // this means that in our usage, CTFrameDraw() will draw at (0,0) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 90c401ee..f2580504 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -34,7 +34,7 @@ uiDrawFontDescriptor defaultFont = { }; uiAttributedString *attrstr; -#define margins 5 +#define margins 10 static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { @@ -113,8 +113,7 @@ int main(void) attrstr = uiNewAttributedString(text); - mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); - uiWindowSetMargined(mainwin, 1); + mainwin = uiNewWindow("libui Text-Drawing Example", 640, 480, 1); uiWindowOnClosing(mainwin, onClosing, NULL); area = uiNewArea(&handler); From 5444f76bd35f275f3d4582e6889bb6eb372ffc07 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 21:06:45 -0500 Subject: [PATCH 052/487] Started implementing the new text layout stuff on GTK+. The drawtext example works. --- examples/drawtext/main.c | 1 + unix/CMakeLists.txt | 2 +- unix/_old_drawtext.c | 217 +++++++++++++++++++++++++ unix/drawtext.c | 339 +++++++++++---------------------------- unix/graphemes.c | 2 +- unix/uipriv_unix.h | 2 + 6 files changed, 318 insertions(+), 245 deletions(-) create mode 100644 unix/_old_drawtext.c diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index f2580504..10f41ad5 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -117,6 +117,7 @@ int main(void) uiWindowOnClosing(mainwin, onClosing, NULL); area = uiNewArea(&handler); + // TODO on GTK+ this doesn't get expand properties set properly? uiWindowSetChild(mainwin, uiControl(area)); uiControlShow(uiControl(mainwin)); diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 9300bcb7..1684e2ce 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -22,7 +22,7 @@ list(APPEND _LIBUI_SOURCES unix/drawtext.c unix/editablecombo.c unix/entry.c - unix/fontbutton.c +# unix/fontbutton.c unix/form.c unix/future.c unix/graphemes.c diff --git a/unix/_old_drawtext.c b/unix/_old_drawtext.c new file mode 100644 index 00000000..7677f076 --- /dev/null +++ b/unix/_old_drawtext.c @@ -0,0 +1,217 @@ +// 6 september 2015 +#include "uipriv_unix.h" +#include "draw.h" + +struct uiDrawFontFamilies { + PangoFontFamily **f; + int n; +}; + +uiDrawFontFamilies *uiDrawListFontFamilies(void) +{ + uiDrawFontFamilies *ff; + PangoFontMap *map; + + ff = uiNew(uiDrawFontFamilies); + map = pango_cairo_font_map_get_default(); + pango_font_map_list_families(map, &(ff->f), &(ff->n)); + // do not free map; it's a shared resource + return ff; +} + +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +{ + return ff->n; +} + +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) +{ + PangoFontFamily *f; + + f = ff->f[n]; + return uiUnixStrdupText(pango_font_family_get_name(f)); +} + +void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) +{ + g_free(ff->f); + uiFree(ff); +} + +struct uiDrawTextFont { + PangoFont *f; +}; + +uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref) +{ + uiDrawTextFont *font; + + font = uiNew(uiDrawTextFont); + font->f = f; + if (ref) + g_object_ref(font->f); + return font; +} + + + +PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc) +{ + PangoFont *f; + PangoContext *context; + + // in this case, the context is necessary for the metrics to be correct + context = mkGenericPangoCairoContext(); + f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc); + if (f == NULL) { + // LONGTERM + g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs"); + } + g_object_unref(context); + return f; +} + +uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +{ + PangoFont *f; + PangoFontDescription *pdesc; + + pdesc = pango_font_description_new(); + pango_font_description_set_family(pdesc, + desc->Family); + pango_font_description_set_size(pdesc, + (gint) (desc->Size * PANGO_SCALE)); + pango_font_description_set_weight(pdesc, + pangoWeights[desc->Weight]); + pango_font_description_set_style(pdesc, + pangoItalics[desc->Italic]); + pango_font_description_set_stretch(pdesc, + pangoStretches[desc->Stretch]); + f = pangoDescToPangoFont(pdesc); + pango_font_description_free(pdesc); + return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref +} + +void uiDrawFreeTextFont(uiDrawTextFont *font) +{ + g_object_unref(font->f); + uiFree(font); +} + +uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +{ + return (uintptr_t) (font->f); +} + +void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +{ + PangoFontDescription *pdesc; + + // this creates a copy; we free it later + pdesc = pango_font_describe(font->f); + + // TODO + + pango_font_description_free(pdesc); +} + +// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description +// Note that we convert to double before dividing to make sure the floating-point stuff is right +#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE) +#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE)) + +void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +{ + PangoFontMetrics *pm; + + pm = pango_font_get_metrics(font->f, NULL); + metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm)); + metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm)); + // Pango doesn't seem to expose this :( Use 0 and hope for the best. + metrics->Leading = 0; + metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm)); + metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm)); + pango_font_metrics_unref(pm); +} + +// 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 *graphemes; + PangoFont *defaultFont; + double width; + PangoAttrList *attrs; +}; + +uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width) +{ + uiDrawTextLayout *layout; + PangoContext *context; + + layout = uiNew(uiDrawTextLayout); + layout->s = g_strdup(text); + context = mkGenericPangoCairoContext(); + layout->graphemes = graphemes(layout->s, context); + g_object_unref(context); + layout->defaultFont = defaultFont->f; + g_object_ref(layout->defaultFont); // retain a copy + uiDrawTextLayoutSetWidth(layout, width); + layout->attrs = pango_attr_list_new(); + return layout; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *layout) +{ + pango_attr_list_unref(layout->attrs); + g_object_unref(layout->defaultFont); + uiFree(layout->graphemes); + g_free(layout->s); + uiFree(layout); +} + +void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) +{ + layout->width = width; +} + +static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl) +{ + // again, this makes a copy + desc = pango_font_describe(layout->defaultFont); + + pango_layout_set_attributes(pl, layout->attrs); +} + +void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) +{ + PangoLayout *pl; + + pl = pango_cairo_create_layout(c->cr); +} + +static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startChar, int endChar) +{ + attr->start_index = layout->graphemes[startChar]; + attr->end_index = layout->graphemes[endChar]; + pango_attr_list_insert(layout->attrs, attr); + // pango_attr_list_insert() takes attr; we don't free it +} + +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +{ + PangoAttribute *attr; + guint16 rr, gg, bb, aa; + + rr = (guint16) (r * 65535); + gg = (guint16) (g * 65535); + bb = (guint16) (b * 65535); + aa = (guint16) (a * 65535); + + attr = pango_attr_foreground_new(rr, gg, bb); + addAttr(layout, attr, startChar, endChar); + + // TODO what if aa == 0? + attr = FUTURE_pango_attr_foreground_alpha_new(aa); + if (attr != NULL) + addAttr(layout, attr, startChar, endChar); +} diff --git a/unix/drawtext.c b/unix/drawtext.c index 7078e1ac..64ae9928 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -1,71 +1,21 @@ -// 6 september 2015 +// 17 january 2017 #include "uipriv_unix.h" #include "draw.h" -struct uiDrawFontFamilies { - PangoFontFamily **f; - int n; +struct uiDrawTextLayout { + PangoLayout *layout; }; -uiDrawFontFamilies *uiDrawListFontFamilies(void) -{ - uiDrawFontFamilies *ff; - PangoFontMap *map; +// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description +// For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double +#define pangoToCairo(pango) (pango_units_to_double(pango)) +#define cairoToPango(cairo) (pango_units_from_double(cairo)) - ff = uiNew(uiDrawFontFamilies); - map = pango_cairo_font_map_get_default(); - pango_font_map_list_families(map, &(ff->f), &(ff->n)); - // do not free map; it's a shared resource - return ff; -} - -int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) -{ - return ff->n; -} - -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) -{ - PangoFontFamily *f; - - f = ff->f[n]; - return uiUnixStrdupText(pango_font_family_get_name(f)); -} - -void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) -{ - g_free(ff->f); - uiFree(ff); -} - -struct uiDrawTextFont { - PangoFont *f; -}; - -uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref) -{ - uiDrawTextFont *font; - - font = uiNew(uiDrawTextFont); - font->f = f; - if (ref) - g_object_ref(font->f); - return font; -} - -static const PangoWeight pangoWeights[] = { - [uiDrawTextWeightThin] = PANGO_WEIGHT_THIN, - [uiDrawTextWeightUltraLight] = PANGO_WEIGHT_ULTRALIGHT, - [uiDrawTextWeightLight] = PANGO_WEIGHT_LIGHT, - [uiDrawTextWeightBook] = PANGO_WEIGHT_BOOK, - [uiDrawTextWeightNormal] = PANGO_WEIGHT_NORMAL, - [uiDrawTextWeightMedium] = PANGO_WEIGHT_MEDIUM, - [uiDrawTextWeightSemiBold] = PANGO_WEIGHT_SEMIBOLD, - [uiDrawTextWeightBold] = PANGO_WEIGHT_BOLD, - [uiDrawTextWeightUltraBold] = PANGO_WEIGHT_ULTRABOLD, - [uiDrawTextWeightHeavy] = PANGO_WEIGHT_HEAVY, - [uiDrawTextWeightUltraHeavy] = PANGO_WEIGHT_ULTRAHEAVY, -}; +// we need a context for a few things +// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent +// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings +// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us +#define mkGenericPangoCairoContext() (gdk_pango_context_get()) static const PangoStyle pangoItalics[] = { [uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL, @@ -85,209 +35,112 @@ static const PangoStretch pangoStretches[] = { [uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, }; -// we need a context for a few things -// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent -// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings -// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us -#define mkGenericPangoCairoContext() (gdk_pango_context_get()) - -PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc) +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { - PangoFont *f; + uiDrawTextLayout *tl; PangoContext *context; - - // in this case, the context is necessary for the metrics to be correct - context = mkGenericPangoCairoContext(); - f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc); - if (f == NULL) { - // LONGTERM - g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs"); - } - g_object_unref(context); - return f; -} - -uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) -{ - PangoFont *f; - PangoFontDescription *pdesc; - - pdesc = pango_font_description_new(); - pango_font_description_set_family(pdesc, - desc->Family); - pango_font_description_set_size(pdesc, - (gint) (desc->Size * PANGO_SCALE)); - pango_font_description_set_weight(pdesc, - pangoWeights[desc->Weight]); - pango_font_description_set_style(pdesc, - pangoItalics[desc->Italic]); - pango_font_description_set_stretch(pdesc, - pangoStretches[desc->Stretch]); - f = pangoDescToPangoFont(pdesc); - pango_font_description_free(pdesc); - return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref -} - -void uiDrawFreeTextFont(uiDrawTextFont *font) -{ - g_object_unref(font->f); - uiFree(font); -} - -uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) -{ - return (uintptr_t) (font->f); -} - -void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) -{ - PangoFontDescription *pdesc; - - // this creates a copy; we free it later - pdesc = pango_font_describe(font->f); - - // TODO - - pango_font_description_free(pdesc); -} - -// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description -// Note that we convert to double before dividing to make sure the floating-point stuff is right -#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE) -#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE)) - -void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) -{ - PangoFontMetrics *pm; - - pm = pango_font_get_metrics(font->f, NULL); - metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm)); - metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm)); - // Pango doesn't seem to expose this :( Use 0 and hope for the best. - metrics->Leading = 0; - metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm)); - metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm)); - pango_font_metrics_unref(pm); -} - -// 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 *graphemes; - PangoFont *defaultFont; - double width; - PangoAttrList *attrs; -}; - -uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width) -{ - uiDrawTextLayout *layout; - PangoContext *context; - - layout = uiNew(uiDrawTextLayout); - layout->s = g_strdup(text); - context = mkGenericPangoCairoContext(); - layout->graphemes = graphemes(layout->s, context); - g_object_unref(context); - layout->defaultFont = defaultFont->f; - g_object_ref(layout->defaultFont); // retain a copy - uiDrawTextLayoutSetWidth(layout, width); - layout->attrs = pango_attr_list_new(); - return layout; -} - -void uiDrawFreeTextLayout(uiDrawTextLayout *layout) -{ - pango_attr_list_unref(layout->attrs); - g_object_unref(layout->defaultFont); - uiFree(layout->graphemes); - g_free(layout->s); - uiFree(layout); -} - -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) -{ - layout->width = width; -} - -static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl) -{ PangoFontDescription *desc; - int width; + int pangoWidth; - pango_layout_set_text(pl, layout->s, -1); - - // again, this makes a copy - desc = pango_font_describe(layout->defaultFont); - // this is safe; the description is copied - pango_layout_set_font_description(pl, desc); - pango_font_description_free(desc); - - width = cairoToPango(layout->width); - if (layout->width < 0) - width = -1; - pango_layout_set_width(pl, width); - - pango_layout_set_attributes(pl, layout->attrs); -} - -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) -{ - PangoContext *context; - PangoLayout *pl; - PangoRectangle logical; + tl = uiNew(uiDrawTextLayout); // in this case, the context is necessary to create the layout // the layout takes a ref on the context so we can unref it afterward context = mkGenericPangoCairoContext(); - pl = pango_layout_new(context); + tl->layout = pango_layout_new(context); g_object_unref(context); - prepareLayout(layout, pl); - pango_layout_get_extents(pl, NULL, &logical); + // this is safe; pango_layout_set_text() copies the string + pango_layout_set_text(tl->layout, uiAttributedStringString(s), -1); - g_object_unref(pl); + desc = pango_font_description_new(); + pango_font_description_set_family(desc, defaultFont->Family); + pango_font_description_set_style(desc, pangoItalics[defaultFont->Italic]); + // for the most part, pango weights correlate to ours + // the differences: + // - Book — libui: 350, Pango: 380 + // - Ultra Heavy — libui: 950, Pango: 1000 + // TODO figure out what to do about this misalignment + pango_font_description_set_weight(desc, defaultFont->Weight); + pango_font_description_set_stretch(desc, pangoStretches[defaultFont->Stretch]); + // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double + pango_font_description_set_size(desc, pango_units_from_double(defaultFont->Size)); + pango_layout_set_font_description(tl->layout, desc); + // this is safe; the description is copied + pango_font_description_free(desc); + pangoWidth = cairoToPango(width); + if (width < 0) + pangoWidth = -1; + pango_layout_set_width(tl->layout, pangoWidth); + + // TODO attributes + + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + g_object_unref(tl->layout); + uiFree(tl); +} + +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + cairo_move_to(c->cr, x, y); + pango_cairo_show_layout(c->cr, tl->layout); +} + +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + PangoRectangle logical; + + pango_layout_get_extents(tl->layout, NULL, &logical); *width = pangoToCairo(logical.width); *height = pangoToCairo(logical.height); } -void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - PangoLayout *pl; - - pl = pango_cairo_create_layout(c->cr); - prepareLayout(layout, pl); - - cairo_move_to(c->cr, x, y); - pango_cairo_show_layout(c->cr, pl); - - g_object_unref(pl); + return pango_layout_get_line_count(tl->layout); } -static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startChar, int endChar) +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - attr->start_index = layout->graphemes[startChar]; - attr->end_index = layout->graphemes[endChar]; - pango_attr_list_insert(layout->attrs, attr); - // pango_attr_list_insert() takes attr; we don't free it + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + *start = pll->start_index; + *end = pll->start_index + pll->length; + // TODO unref? } -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - PangoAttribute *attr; - guint16 rr, gg, bb, aa; + PangoLayoutLine *pll; - rr = (guint16) (r * 65535); - gg = (guint16) (g * 65535); - bb = (guint16) (b * 65535); - aa = (guint16) (a * 65535); - - attr = pango_attr_foreground_new(rr, gg, bb); - addAttr(layout, attr, startChar, endChar); - - // TODO what if aa == 0? - attr = FUTURE_pango_attr_foreground_alpha_new(aa); - if (attr != NULL) - addAttr(layout, attr, startChar, endChar); + pll = pango_layout_get_line_readonly(tl->layout, line); + // TODO unref? +} + +// TODO +#if 0 +{ + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + // TODO unref? +} +#endif + +void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) +{ +} + +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) +{ +} + +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) +{ } diff --git a/unix/graphemes.c b/unix/graphemes.c index e489ebff..4f957352 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -6,7 +6,7 @@ int graphemesTakesUTF16(void) return 0; } -struct graphemes *graphemes(void *s, size_t len); +struct graphemes *graphemes(void *s, size_t len) { struct graphemes *g; char *text = (char *) s; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index de2c1d3d..0a477158 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -46,8 +46,10 @@ extern uiDrawContext *newContext(cairo_t *); extern void freeContext(uiDrawContext *); // drawtext.c +#if 0 /* TODO */ extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); +#endif // image.c /*TODO remove this*/typedef struct uiImage uiImage; From f7121774e175ea3f450eaa97a9dbcd3bfe970601 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 23:25:26 -0500 Subject: [PATCH 053/487] Added some TODOs and started the work necessary for the Windows text system migration... which is gonna hurt. --- test/drawtests.c | 1 + windows/CMakeLists.txt | 4 ++-- windows/{drawtext.cpp => _old_drawtext.cpp} | 0 windows/_uipriv_migrate.hpp | 6 ++++++ windows/drawpath.cpp | 1 + windows/dwrite.cpp | 6 +++++- 6 files changed, 15 insertions(+), 3 deletions(-) rename windows/{drawtext.cpp => _old_drawtext.cpp} (100%) diff --git a/test/drawtests.c b/test/drawtests.c index b6de753f..5c409294 100644 --- a/test/drawtests.c +++ b/test/drawtests.c @@ -4,6 +4,7 @@ // TODO // - test multiple clips // - test saving and restoring clips +// - copy tests from https://github.com/Microsoft/WinObjC struct drawtest { const char *name; diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 4695eb4f..5b1232ed 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -26,8 +26,8 @@ list(APPEND _LIBUI_SOURCES windows/editablecombo.cpp windows/entry.cpp windows/events.cpp - windows/fontbutton.cpp - windows/fontdialog.cpp +# windows/fontbutton.cpp +# windows/fontdialog.cpp windows/form.cpp windows/graphemes.cpp windows/grid.cpp diff --git a/windows/drawtext.cpp b/windows/_old_drawtext.cpp similarity index 100% rename from windows/drawtext.cpp rename to windows/_old_drawtext.cpp diff --git a/windows/_uipriv_migrate.hpp b/windows/_uipriv_migrate.hpp index 13d3670e..11b737d1 100644 --- a/windows/_uipriv_migrate.hpp +++ b/windows/_uipriv_migrate.hpp @@ -19,6 +19,7 @@ extern IDWriteFactory *dwfactory; #endif extern HRESULT initDrawText(void); extern void uninitDrawText(void); +#if 0 /* TODO */ #ifdef __cplusplus struct fontCollection { IDWriteFontCollection *fonts; @@ -30,8 +31,10 @@ extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *fa extern void fontCollectionFree(fontCollection *fc); extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); #endif +#endif // drawtext.cpp +#if 0 /* TODO */ #ifdef __cplusplus extern uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL copyFamily, double size); struct dwriteAttr { @@ -45,8 +48,10 @@ struct dwriteAttr { extern void attrToDWriteAttr(struct dwriteAttr *attr); extern void dwriteAttrToAttr(struct dwriteAttr *attr); #endif +#endif // fontdialog.cpp +#if 0 /* TODO */ #ifdef __cplusplus struct fontDialogParams { IDWriteFont *font; @@ -59,3 +64,4 @@ extern void loadInitialFontDialogParams(struct fontDialogParams *params); extern void destroyFontDialogParams(struct fontDialogParams *params); extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); #endif +#endif diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index 49855be6..045d11c0 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -66,6 +66,7 @@ void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) // That is to say, it's NOT THE SWEEP. // The sweep is defined by the start and end points and whether the arc is "large". // As a result, this design does not allow for full circles or ellipses with a single arc; they have to be simulated with two. +// TODO https://github.com/Microsoft/WinObjC/blob/develop/Frameworks/CoreGraphics/CGPath.mm#L313 struct arc { double xCenter; diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 9156f179..6e9e5835 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -1,6 +1,6 @@ // 14 april 2016 #include "uipriv_windows.hpp" -// TODO really migrate? +// TODO really migrate? (TODO what did I mean by this?) IDWriteFactory *dwfactory = NULL; @@ -17,6 +17,8 @@ void uninitDrawText(void) dwfactory->Release(); } +#if 0 /* TODO */ + fontCollection *loadFontCollection(void) { fontCollection *fc; @@ -86,3 +88,5 @@ void fontCollectionFree(fontCollection *fc) fc->fonts->Release(); uiFree(fc); } + +#endif From cac390a82153fe148b285aa854f96e7212ec7f42 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 19 Jan 2017 21:13:03 -0500 Subject: [PATCH 054/487] Wrote much of the new text layout code on Windows. Now to test. --- windows/_old_drawtext.cpp | 200 +------------------------------ windows/areaevents.cpp | 2 + windows/drawtext.cpp | 244 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 198 deletions(-) create mode 100644 windows/drawtext.cpp diff --git a/windows/_old_drawtext.cpp b/windows/_old_drawtext.cpp index 05a24f67..a10e1ad6 100644 --- a/windows/_old_drawtext.cpp +++ b/windows/_old_drawtext.cpp @@ -77,152 +77,8 @@ uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL cop return font; } -// TODO consider moving these all to dwrite.cpp - // TODO MinGW-w64 is missing this one #define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT(350)) -static const struct { - bool lastOne; - uiDrawTextWeight uival; - DWRITE_FONT_WEIGHT dwval; -} dwriteWeights[] = { - { false, uiDrawTextWeightThin, DWRITE_FONT_WEIGHT_THIN }, - { false, uiDrawTextWeightUltraLight, DWRITE_FONT_WEIGHT_ULTRA_LIGHT }, - { false, uiDrawTextWeightLight, DWRITE_FONT_WEIGHT_LIGHT }, - { false, uiDrawTextWeightBook, DWRITE_FONT_WEIGHT_SEMI_LIGHT }, - { false, uiDrawTextWeightNormal, DWRITE_FONT_WEIGHT_NORMAL }, - { false, uiDrawTextWeightMedium, DWRITE_FONT_WEIGHT_MEDIUM }, - { false, uiDrawTextWeightSemiBold, DWRITE_FONT_WEIGHT_SEMI_BOLD }, - { false, uiDrawTextWeightBold, DWRITE_FONT_WEIGHT_BOLD }, - { false, uiDrawTextWeightUltraBold, DWRITE_FONT_WEIGHT_ULTRA_BOLD }, - { false, uiDrawTextWeightHeavy, DWRITE_FONT_WEIGHT_HEAVY }, - { true, uiDrawTextWeightUltraHeavy, DWRITE_FONT_WEIGHT_ULTRA_BLACK, }, -}; - -static const struct { - bool lastOne; - uiDrawTextItalic uival; - DWRITE_FONT_STYLE dwval; -} dwriteItalics[] = { - { false, uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, - { false, uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, - { true, uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, -}; - -static const struct { - bool lastOne; - uiDrawTextStretch uival; - DWRITE_FONT_STRETCH dwval; -} dwriteStretches[] = { - { false, uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, - { false, uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, - { false, uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, - { false, uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, - { false, uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, - { false, uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, - { false, uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, - { false, uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, - { true, uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, -}; - -void attrToDWriteAttr(struct dwriteAttr *attr) -{ - bool found; - int i; - - found = false; - for (i = 0; ; i++) { - if (dwriteWeights[i].uival == attr->weight) { - attr->dweight = dwriteWeights[i].dwval; - found = true; - break; - } - if (dwriteWeights[i].lastOne) - break; - } - if (!found) - userbug("Invalid text weight %d passed to text function.", attr->weight); - - found = false; - for (i = 0; ; i++) { - if (dwriteItalics[i].uival == attr->italic) { - attr->ditalic = dwriteItalics[i].dwval; - found = true; - break; - } - if (dwriteItalics[i].lastOne) - break; - } - if (!found) - userbug("Invalid text italic %d passed to text function.", attr->italic); - - found = false; - for (i = 0; ; i++) { - if (dwriteStretches[i].uival == attr->stretch) { - attr->dstretch = dwriteStretches[i].dwval; - found = true; - break; - } - if (dwriteStretches[i].lastOne) - break; - } - if (!found) - // TODO on other platforms too - userbug("Invalid text stretch %d passed to text function.", attr->stretch); -} - -void dwriteAttrToAttr(struct dwriteAttr *attr) -{ - int weight, against, n; - int curdiff, curindex; - bool found; - int i; - - // weight is scaled; we need to test to see what's nearest - weight = (int) (attr->dweight); - against = (int) (dwriteWeights[0].dwval); - curdiff = abs(against - weight); - curindex = 0; - for (i = 1; ; i++) { - against = (int) (dwriteWeights[i].dwval); - n = abs(against - weight); - if (n < curdiff) { - curdiff = n; - curindex = i; - } - if (dwriteWeights[i].lastOne) - break; - } - attr->weight = dwriteWeights[i].uival; - - // italic and stretch are simple values; we can just do a matching search - found = false; - for (i = 0; ; i++) { - if (dwriteItalics[i].dwval == attr->ditalic) { - attr->italic = dwriteItalics[i].uival; - found = true; - break; - } - if (dwriteItalics[i].lastOne) - break; - } - if (!found) - // these are implbug()s because users shouldn't be able to get here directly; TODO? - implbug("invalid italic %d passed to dwriteAttrToAttr()", attr->ditalic); - - found = false; - for (i = 0; ; i++) { - if (dwriteStretches[i].dwval == attr->dstretch) { - attr->stretch = dwriteStretches[i].uival; - found = true; - break; - } - if (dwriteStretches[i].lastOne) - break; - } - if (!found) - implbug("invalid stretch %d passed to dwriteAttrToAttr()", attr->dstretch); -} uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) { @@ -357,13 +213,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultF defaultFont->f->GetWeight(), defaultFont->f->GetStyle(), defaultFont->f->GetStretch(), - // typographic points are 1/72 inch; this parameter is 1/96 inch - // fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx - defaultFont->size * (96.0 / 72.0), - // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx - // TODO use the current locale again? - L"", - &(layout->format)); + if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); @@ -417,18 +267,8 @@ IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt IDWriteTextLayout *dl; DWRITE_TEXT_RANGE range; IUnknown *unkBrush; - DWRITE_WORD_WRAPPING wrap; - FLOAT maxWidth; HRESULT hr; - hr = dwfactory->CreateTextLayout(layout->text, layout->textlen, - layout->format, - // FLOAT is float, not double, so this should work... TODO - FLT_MAX, FLT_MAX, - &dl); - if (hr != S_OK) - logHRESULT(L"error creating IDWriteTextLayout", hr); - for (const struct layoutAttr &attr : *(layout->attrs)) { range.startPosition = layout->graphemes[attr.start]; range.length = layout->graphemes[attr.end] - layout->graphemes[attr.start]; @@ -452,48 +292,12 @@ IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt logHRESULT(L"error adding attribute to text layout", hr); } - // and set the width - // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 - wrap = DWRITE_WORD_WRAPPING_WRAP; - maxWidth = layout->width; - if (layout->width < 0) { - wrap = DWRITE_WORD_WRAPPING_NO_WRAP; - // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe - maxWidth = FLT_MAX; // see TODO above - } - hr = dl->SetWordWrapping(wrap); - if (hr != S_OK) - logHRESULT(L"error setting word wrapping mode", hr); - hr = dl->SetMaxWidth(maxWidth); - if (hr != S_OK) - logHRESULT(L"error setting max layout width", hr); + return dl; } -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) -{ - layout->width = width; -} - -// TODO for a single line the height includes the leading; it should not -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) -{ - IDWriteTextLayout *dl; - DWRITE_TEXT_METRICS metrics; - HRESULT hr; - - dl = prepareLayout(layout, NULL); - hr = dl->GetMetrics(&metrics); - if (hr != S_OK) - logHRESULT(L"error getting layout metrics", hr); - *width = metrics.width; - // TODO make sure the behavior of this on empty strings is the same on all platforms - *height = metrics.height; - dl->Release(); -} - void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) { IDWriteTextLayout *dl; diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 7d391b85..615c06ea 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -2,6 +2,8 @@ #include "uipriv_windows.hpp" #include "area.hpp" +// TODO https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp notes on explicit RTL handling under MirrorXCoordinate(); also in areadraw.cpp too? + static uiModifiers getModifiers(void) { uiModifiers m = 0; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp new file mode 100644 index 00000000..3932af52 --- /dev/null +++ b/windows/drawtext.cpp @@ -0,0 +1,244 @@ +// 17 january 2017 +#include "uipriv_windows.hpp" + +struct uiDrawTextLayout { + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + UINT32 *nLines; + struct lineInfo *lineInfo; + // for converting DirectWrite indices to byte offsets + size_t *u16tou8; + size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary? +}; + +// typographic points are 1/72 inch; this parameter is 1/96 inch +// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx +#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) + +static const DWRITE_FONT_STYLE dwriteItalics[] = { + [uiDrawTextItalicNormal] = DWRITE_FONT_STYLE_NORMAL, + [uiDrawTextItalicOblique] = DWRITE_FONT_STYLE_OBLIQUE, + [uiDrawTextItalicItalic] = DWRITE_FONT_STYLE_ITALIC, +}; + +static const DWRITE_FONT_STRETCH dwriteStretches[] = { + [uiDrawTextStretchUltraCondensed] = DWRITE_FONT_STRETCH_ULTRA_CONDENSED, + [uiDrawTextStretchExtraCondensed] = DWRITE_FONT_STRETCH_EXTRA_CONDENSED, + [uiDrawTextStretchCondensed] = DWRITE_FONT_STRETCH_CONDENSED, + [uiDrawTextStretchSemiCondensed] = DWRITE_FONT_STRETCH_SEMI_CONDENSED, + [uiDrawTextStretchNormal] = DWRITE_FONT_STRETCH_NORMAL, + [uiDrawTextStretchSemiExpanded] = DWRITE_FONT_STRETCH_SEMI_EXPANDED, + [uiDrawTextStretchExpanded] = DWRITE_FONT_STRETCH_EXPANDED, + [uiDrawTextStretchExtraExpanded] = DWRITE_FONT_STRETCH_EXTRA_EXPANDED, + [uiDrawTextStretchUltraExpanded] = DWRITE_FONT_STRETCH_ULTRA_EXPANDED, +}; + +struct lineInfo { + size_t startPos; + size_t endPos; + size_t newlineCount; + double x; + double y; + double width; + double height; + double baseline; +}; + +// this function is deeply indebted to the PadWrite sample: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp +static void computeLineInfo(uiDrawTextLayout *tl) +{ + DWRITE_LINE_METRICS *dlm; + size_t nextStart; + UINT32 i; + DWRITE_HIT_TEST_METRICS htm; + UINT32 unused; + HRESULT hr; + + // TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead + hr = tl->layout->GetLineMetrics(NULL, 0, &(tl->nLines)); + switch (hr) { + case S_OK: + // TODO what do we do here + case E_NOT_SUFFICIENT_BUFFER: + break; + default: + logHRESULT(L"error getting number of lines in IDWriteTextLayout", hr); + } + tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)"); + + dlm = new DWRITE_LINE_METRICS[tl->nLines]; + // TODO make sure pasisng NULL here is legal + hr = tl->layout->GetLineMetrics(dlm, tl->nLines, NULL); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout line metrics", hr); + + // assume the first line starts at position 0 and the string flow is incremental + nextStart = 0; + for (i = 0; i < tl->nLines; i++) { + tl->lineinfo[i].startPos = nextStart; + tl->lineinfo[i].endPos = nextStart + dlm[i].length; + tl->lineinfo[i].newlineCount = dlm[i].newlineLength; + nextStart = tl->lineinfo[i].endpos; + + hr = layout->HitTestTextRange(line->startPos, (line->endPos - line->newlineCount) - line->startPos, + 0, 0, + &htm, 1, &unused); + if (hr == E_NOT_SUFFICIENT_BUFFER) + logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout line rect", hr); + // TODO verify htm.textPosition and htm.length? + tl->lineInfo[i].x = htm.left; + tl->lineInfo[i].y = htm.top; + tl->lineInfo[i].width = htm.width; + tl->lineInfo[i].height = htm.height; + // TODO verify dlm[i].height == htm.height + + // TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative? + // TODO and even on those platforms can we somehow split tyographic leading from spacing? + // TODO and on that note, can we have both line spacing proportionally above and uniformly below? + tl->lineinfo[i].baseline = dlm[i].baseline; + } + + delete[] dlm; +} + +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +{ + uiDrawTextLayout *tl; + WCHAR *wDefaultFamily; + DWRITE_WORD_WRAPPING wrap; + FLOAT maxWidth; + HRESULT hr; + + tl = uiNew(uiDrawTextLayout); + + wDefaultFamily = toUTF16(defaultFont->Family); + hr = dwfactory->CreateTextFormat( + wDefaultFamily, NULL, + // for the most part, DirectWrite weights correlate to ours + // the differences: + // - Minimum — libui: 0, DirectWrite: 1 + // - Maximum — libui: 1000, DirectWrite: 999 + // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) + (DWRITE_FONT_WEIGHT) (defaultFont->Weight), + dwriteItalics[defaultFont->Italic], + dwriteStrecthes[defaultFont->Stretch], + pointSizeToDWriteSize(defaultFont->Size), + // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx + // TODO use the current locale? + L"", + &(tl->format)); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextFormat", hr); + + hr = dwfactory->CreateTextLayout( + attrstrUTF16(s), attrstrUTF16Len(s), + tl->format, + // FLOAT is float, not double, so this should work... TODO + FLT_MAX, FLT_MAX, + &(tl->layout)); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextLayout", hr); + + // and set the width + // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway) + wrap = DWRITE_WORD_WRAPPING_WRAP; + maxWidth = (FLOAT) width; + if (width < 0) { + // TODO is this wrapping juggling even necessary? + wrap = DWRITE_WORD_WRAPPING_NO_WRAP; + // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe + maxWidth = FLT_MAX; // see TODO above + } + hr = tl->layout->SetWordWrapping(wrap); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout word wrapping mode", hr); + hr = tl->layout->SetMaxWidth(maxWidth); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); + + computeLineInfo(tl); + + // and finally copy the UTF-16 to UTF-8 index conversion table + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + + // TODO can/should this be moved elsewhere? + uiFree(wDefaultFamily); + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + uiFree(tl->u16tou8); + uiFree(tl->lineInfo); + tl->layout->Release(); + tl->format->Release(); + uiFree(tl); +} + +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + D2D1_POINT_2F pt; + ID2D1Brush *black; + + // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms + // TODO figure out if this needs to be cleaned out + black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); + + pt.x = x; + pt.y = y; + // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? + // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP? + // TODO when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? + // TODO what is our pixel snapping setting related to the OPTIONS enum values? + c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); + + black->Release(); +} + +// TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work... +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + DWRITE_TEXT_METRICS metrics; + HRESULT hr; + + hr = tl->layout->GetMetrics(&metrics); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout layout metrics", hr); + *width = metrics.width; + // TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too) + *height = metrics.height; +} + +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return tl->nLines; +} + +// DirectWrite doesn't provide a direct way to do this, so we have to do this manually +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + *start = tl->lineInfo[line].startPos; + *start = tl->u16tou8[*start]; + *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; + *end = tl->u16tou8[*end]; +} + +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) +{ + // TODO +} + +void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) +{ +} + +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) +{ + // TODO +} + +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) +{ +} From c0781a13ae5839617910c45f79d1297cb89810c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 03:24:06 -0500 Subject: [PATCH 055/487] Fixed compile errors. Jesus. Runtime errors next. --- common/uipriv.h | 4 +- windows/_old_drawtext.cpp | 24 ----------- windows/drawtext.cpp | 85 ++++++++++++++++++++++++++------------- windows/graphemes.cpp | 3 +- windows/utf16.cpp | 2 +- 5 files changed, 62 insertions(+), 56 deletions(-) diff --git a/common/uipriv.h b/common/uipriv.h index 5ed23bf5..e62e86f8 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,5 +1,8 @@ // 6 april 2015 +// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors +#include "utf.h" + #ifdef __cplusplus extern "C" { #endif @@ -7,7 +10,6 @@ extern "C" { #include #include #include "controlsigs.h" -#include "utf.h" extern uiInitOptions options; diff --git a/windows/_old_drawtext.cpp b/windows/_old_drawtext.cpp index a10e1ad6..dec0478d 100644 --- a/windows/_old_drawtext.cpp +++ b/windows/_old_drawtext.cpp @@ -237,30 +237,6 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *layout) uiFree(layout); } -static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) -{ - D2D1_BRUSH_PROPERTIES props; - D2D1_COLOR_F color; - ID2D1SolidColorBrush *brush; - HRESULT hr; - - ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); - props.opacity = 1.0; - // identity matrix - props.transform._11 = 1; - props.transform._22 = 1; - color.r = r; - color.g = g; - color.b = b; - color.a = a; - hr = rt->CreateSolidColorBrush( - &color, - &props, - &brush); - if (hr != S_OK) - logHRESULT(L"error creating solid brush", hr); - return brush; -} IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt) { diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 3932af52..8877396e 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -1,36 +1,41 @@ // 17 january 2017 #include "uipriv_windows.hpp" +#include "draw.hpp" struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; - UINT32 *nLines; + UINT32 nLines; struct lineInfo *lineInfo; // for converting DirectWrite indices to byte offsets size_t *u16tou8; size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary? }; +// TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here + // typographic points are 1/72 inch; this parameter is 1/96 inch // fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) -static const DWRITE_FONT_STYLE dwriteItalics[] = { - [uiDrawTextItalicNormal] = DWRITE_FONT_STYLE_NORMAL, - [uiDrawTextItalicOblique] = DWRITE_FONT_STYLE_OBLIQUE, - [uiDrawTextItalicItalic] = DWRITE_FONT_STYLE_ITALIC, +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteItalics = { + { uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, + { uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, + { uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, }; -static const DWRITE_FONT_STRETCH dwriteStretches[] = { - [uiDrawTextStretchUltraCondensed] = DWRITE_FONT_STRETCH_ULTRA_CONDENSED, - [uiDrawTextStretchExtraCondensed] = DWRITE_FONT_STRETCH_EXTRA_CONDENSED, - [uiDrawTextStretchCondensed] = DWRITE_FONT_STRETCH_CONDENSED, - [uiDrawTextStretchSemiCondensed] = DWRITE_FONT_STRETCH_SEMI_CONDENSED, - [uiDrawTextStretchNormal] = DWRITE_FONT_STRETCH_NORMAL, - [uiDrawTextStretchSemiExpanded] = DWRITE_FONT_STRETCH_SEMI_EXPANDED, - [uiDrawTextStretchExpanded] = DWRITE_FONT_STRETCH_EXPANDED, - [uiDrawTextStretchExtraExpanded] = DWRITE_FONT_STRETCH_EXTRA_EXPANDED, - [uiDrawTextStretchUltraExpanded] = DWRITE_FONT_STRETCH_ULTRA_EXPANDED, +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteStretches = { + { uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, + { uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, + { uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, + { uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, + { uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, + { uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, + { uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, + { uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, + { uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, }; struct lineInfo { @@ -56,14 +61,11 @@ static void computeLineInfo(uiDrawTextLayout *tl) // TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead hr = tl->layout->GetLineMetrics(NULL, 0, &(tl->nLines)); - switch (hr) { - case S_OK: + // ugh, HRESULT_TO_WIN32() is an inline function and is not constexpr so we can't use switch here + if (hr == S_OK) { // TODO what do we do here - case E_NOT_SUFFICIENT_BUFFER: - break; - default: + } else if (hr != E_NOT_SUFFICIENT_BUFFER) logHRESULT(L"error getting number of lines in IDWriteTextLayout", hr); - } tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)"); dlm = new DWRITE_LINE_METRICS[tl->nLines]; @@ -75,12 +77,12 @@ static void computeLineInfo(uiDrawTextLayout *tl) // assume the first line starts at position 0 and the string flow is incremental nextStart = 0; for (i = 0; i < tl->nLines; i++) { - tl->lineinfo[i].startPos = nextStart; - tl->lineinfo[i].endPos = nextStart + dlm[i].length; - tl->lineinfo[i].newlineCount = dlm[i].newlineLength; - nextStart = tl->lineinfo[i].endpos; + tl->lineInfo[i].startPos = nextStart; + tl->lineInfo[i].endPos = nextStart + dlm[i].length; + tl->lineInfo[i].newlineCount = dlm[i].newlineLength; + nextStart = tl->lineInfo[i].endPos; - hr = layout->HitTestTextRange(line->startPos, (line->endPos - line->newlineCount) - line->startPos, + hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, 0, 0, &htm, 1, &unused); if (hr == E_NOT_SUFFICIENT_BUFFER) @@ -97,7 +99,7 @@ static void computeLineInfo(uiDrawTextLayout *tl) // TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative? // TODO and even on those platforms can we somehow split tyographic leading from spacing? // TODO and on that note, can we have both line spacing proportionally above and uniformly below? - tl->lineinfo[i].baseline = dlm[i].baseline; + tl->lineInfo[i].baseline = dlm[i].baseline; } delete[] dlm; @@ -123,7 +125,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) (DWRITE_FONT_WEIGHT) (defaultFont->Weight), dwriteItalics[defaultFont->Italic], - dwriteStrecthes[defaultFont->Stretch], + dwriteStretches[defaultFont->Stretch], pointSizeToDWriteSize(defaultFont->Size), // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx // TODO use the current locale? @@ -133,7 +135,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto logHRESULT(L"error creating IDWriteTextFormat", hr); hr = dwfactory->CreateTextLayout( - attrstrUTF16(s), attrstrUTF16Len(s), + (const WCHAR *) attrstrUTF16(s), attrstrUTF16Len(s), tl->format, // FLOAT is float, not double, so this should work... TODO FLT_MAX, FLT_MAX, @@ -177,6 +179,31 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } +static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +{ + D2D1_BRUSH_PROPERTIES props; + D2D1_COLOR_F color; + ID2D1SolidColorBrush *brush; + HRESULT hr; + + ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); + props.opacity = 1.0; + // identity matrix + props.transform._11 = 1; + props.transform._22 = 1; + color.r = r; + color.g = g; + color.b = b; + color.a = a; + hr = rt->CreateSolidColorBrush( + &color, + &props, + &brush); + if (hr != S_OK) + logHRESULT(L"error creating solid brush", hr); + return brush; +} + void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index d4fc1e1b..a4aff059 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -57,7 +57,7 @@ struct graphemes *graphemes(void *s, size_t len) g = uiNew(struct graphemes); - hr = itemize(str, len, &items, &n); + hr = itemize(str, len, &items, &nItems); if (hr != S_OK) logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); g->len = nItems; @@ -72,6 +72,7 @@ struct graphemes *graphemes(void *s, size_t len) SCRIPT_ITEM *curItem, *nextItem; SCRIPT_LOGATTR *logattr; size_t *curGTP; + int i; curItem = items + curItemIndex; nextItem = curItem + 1; diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 6271fff7..21d2f8a5 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -36,7 +36,7 @@ char *toUTF8(const WCHAR *wstr) str = (char *) uiAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { - wstr = utf16DecodeRune(wstr, &rune); + wstr = utf16DecodeRune(wstr, 0, &rune); n = utf8EncodeRune(rune, sp); sp += n; } From 88ea7c4665a661edc2d72da16090ac2f336cfa5e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 04:34:15 -0500 Subject: [PATCH 056/487] Fixed runtime errors. Yay it works on all platforms now! Also more TODOs. Now the real work is getting the rest of the functionality in. --- windows/drawtext.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 8877396e..1e35a582 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -69,8 +69,9 @@ static void computeLineInfo(uiDrawTextLayout *tl) tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)"); dlm = new DWRITE_LINE_METRICS[tl->nLines]; - // TODO make sure pasisng NULL here is legal - hr = tl->layout->GetLineMetrics(dlm, tl->nLines, NULL); + // we can't pass NULL here; it outright crashes if we do + // TODO verify the numbers haven't changed + hr = tl->layout->GetLineMetrics(dlm, tl->nLines, &unused); if (hr != S_OK) logHRESULT(L"error getting IDWriteTextLayout line metrics", hr); @@ -204,6 +205,7 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl return brush; } +// TODO this ignores clipping? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; From 2d09f2293295793ca3989be3842a95faf7357e64 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 12:46:00 -0500 Subject: [PATCH 057/487] Added a debugging guide for Windows. Will have to investigate this more, because I can't find any information online that suggests Direct2D should not respect clipping when drawing text :S Also more TODOs. --- examples/drawtext/main.c | 15 +++++++++++++++ windows/graphemes.cpp | 2 ++ 2 files changed, 17 insertions(+) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 10f41ad5..d01d96c6 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -49,6 +49,21 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) uiDrawClip(p->Context, path); uiDrawFreePath(path); + // TODO get rid of this later + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, -100, -100, + p->AreaWidth * 2, + p->AreaHeight * 2); + uiDrawPathEnd(path); + uiDrawBrush b; + b.Type = uiDrawBrushTypeSolid; + b.R = 0.0; + b.G = 1.0; + b.B = 0.0; + b.A = 1.0; + uiDrawFill(p->Context, path, &b); + uiDrawFreePath(path); + layout = uiDrawNewTextLayout(attrstr, &defaultFont, p->AreaWidth - 2 * margins); diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index a4aff059..2b93fab4 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -5,6 +5,8 @@ // So let's use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html) // See also http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/ for more details. +// TODO the DirectWrite equivalent appears to be https://msdn.microsoft.com/en-us/library/windows/desktop/dd316625(v=vs.85).aspx but is somehow somewhat more complicated to use than Uniscribe is! maybe the PadWrite sample uses it? or should we just keep using Uniscribe? + int graphemesTakesUTF16(void) { return 1; From 6ef6ed8cdeb28882c15646a4bc3ff86b310b1cf6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 13:25:21 -0500 Subject: [PATCH 058/487] Expanded the drawtext example to allow for multiple examples and options in the examples. Our old makefiles wouldn't allow examples to be spread across multiple files like this, so yay cmake? --- examples/CMakeLists.txt | 3 + examples/drawtext/basic.c | 81 +++++++++++++++++++++++++ examples/drawtext/drawtext.h | 17 ++++++ examples/drawtext/main.c | 113 ++++++++++++++--------------------- 4 files changed, 145 insertions(+), 69 deletions(-) create mode 100644 examples/drawtext/basic.c create mode 100644 examples/drawtext/drawtext.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 75b7f9d1..430224d8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,9 +32,12 @@ if(NOT WIN32) endif() _add_example(drawtext + drawtext/basic.c drawtext/main.c ${_EXAMPLE_RESOURCES_RC} ) +target_include_directories(drawtext + PRIVATE drawtext) add_custom_target(examples DEPENDS diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c new file mode 100644 index 00000000..2d3d7f3d --- /dev/null +++ b/examples/drawtext/basic.c @@ -0,0 +1,81 @@ +// 17 january 2017 +#include "drawtext.h" + +static const char *text = + "It is with a kind of fear that I begin to write the history of my life. " + "I have, as it were, a superstitious hesitation in lifting the veil that " + "clings about my childhood like a golden mist. The task of writing an " + "autobiography is a difficult one. When I try to classify my earliest " + "impressions, I find that fact and fancy look alike across the years that " + "link the past with the present. The woman paints the child's experiences " + "in her own fantasy. A few impressions stand out vividly from the first " + "years of my life; but \"the shadows of the prison-house are on the rest.\" " + "Besides, many of the joys and sorrows of childhood have lost their " + "poignancy; and many incidents of vital importance in my early education " + "have been forgotten in the excitement of great discoveries. In order, " + "therefore, not to be tedious I shall try to present in a series of " + "sketches only the episodes that seem to me to be the most interesting " + "and important." + ""; +static char fontFamily[] = "Palatino"; +// TODO should be const; look at constructor function +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 18, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiAttributedString *attrstr; + +#define margins 10 + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + // TODO get rid of this later + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, -100, -100, + p->AreaWidth * 2, + p->AreaHeight * 2); + uiDrawPathEnd(path); + uiDrawBrush b; + b.Type = uiDrawBrushTypeSolid; + b.R = 0.0; + b.G = 1.0; + b.B = 0.0; + b.A = 1.0; + uiDrawFill(p->Context, path, &b); + uiDrawFreePath(path); + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + p->AreaWidth - 2 * margins); + uiDrawText(p->Context, layout, margins, margins); + uiDrawFreeTextLayout(layout); +} + +static struct example basicExample; + +struct example *mkBasicExample(void) +{ + basicExample.name = "Basic Paragraph of Text"; + basicExample.panel = uiControl(uiNewVerticalBox()); + basicExample.draw = draw; + + attrstr = uiNewAttributedString(text); + + return &basicExample; +} + +// TODO on GTK+ an area by itself in a window doesn't get expand properties set properly? diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h new file mode 100644 index 00000000..adbac19b --- /dev/null +++ b/examples/drawtext/drawtext.h @@ -0,0 +1,17 @@ +// 20 january 2017 +#include +#include +#include "../../ui.h" + +struct example { + const char *name; + uiControl *panel; + void (*draw)(uiAreaDrawParams *p); + // TODO mouse and key? +}; + +// main.c +extern void redraw(void); + +// basic.c +extern struct example *mkBasicExample(void); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index d01d96c6..ebc9210a 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -1,74 +1,32 @@ // 17 january 2017 -#include -#include -#include "../../ui.h" +#include "drawtext.h" -uiWindow *mainwin; -uiArea *area; -uiAreaHandler handler; +static uiWindow *mainwin; +static uiBox *box; +static uiCombobox *exampleList; +static uiArea *area; +static uiAreaHandler handler; -const char *text = - "It is with a kind of fear that I begin to write the history of my life. " - "I have, as it were, a superstitious hesitation in lifting the veil that " - "clings about my childhood like a golden mist. The task of writing an " - "autobiography is a difficult one. When I try to classify my earliest " - "impressions, I find that fact and fancy look alike across the years that " - "link the past with the present. The woman paints the child's experiences " - "in her own fantasy. A few impressions stand out vividly from the first " - "years of my life; but \"the shadows of the prison-house are on the rest.\" " - "Besides, many of the joys and sorrows of childhood have lost their " - "poignancy; and many incidents of vital importance in my early education " - "have been forgotten in the excitement of great discoveries. In order, " - "therefore, not to be tedious I shall try to present in a series of " - "sketches only the episodes that seem to me to be the most interesting " - "and important." - ""; -char fontFamily[] = "Palatino"; -// TODO should be const; look at constructor function -uiDrawFontDescriptor defaultFont = { - .Family = fontFamily, - .Size = 18, - .Weight = uiDrawTextWeightNormal, - .Italic = uiDrawTextItalicNormal, - .Stretch = uiDrawTextStretchNormal, -}; -uiAttributedString *attrstr; +#define nExamples 20 +static struct example *examples[nExamples]; +static int curExample = 0; -#define margins 10 +static void onExampleChanged(uiCombobox *c, void *data) +{ + uiControlHide(examples[curExample]->panel); + curExample = uiComboboxSelected(exampleList); + uiControlShow(examples[curExample]->panel); + redraw(); +} + +void redraw(void) +{ + uiAreaQueueRedrawAll(area); +} static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { - uiDrawPath *path; - uiDrawTextLayout *layout; - - path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathAddRectangle(path, margins, margins, - p->AreaWidth - 2 * margins, - p->AreaHeight - 2 * margins); - uiDrawPathEnd(path); - uiDrawClip(p->Context, path); - uiDrawFreePath(path); - - // TODO get rid of this later - path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathAddRectangle(path, -100, -100, - p->AreaWidth * 2, - p->AreaHeight * 2); - uiDrawPathEnd(path); - uiDrawBrush b; - b.Type = uiDrawBrushTypeSolid; - b.R = 0.0; - b.G = 1.0; - b.B = 0.0; - b.A = 1.0; - uiDrawFill(p->Context, path, &b); - uiDrawFreePath(path); - - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - p->AreaWidth - 2 * margins); - uiDrawText(p->Context, layout, margins, margins); - uiDrawFreeTextLayout(layout); + examples[curExample]->draw(p); } static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) @@ -109,6 +67,7 @@ int main(void) { uiInitOptions o; const char *err; + int n; handler.Draw = handlerDraw; handler.MouseEvent = handlerMouseEvent; @@ -126,18 +85,34 @@ int main(void) uiOnShouldQuit(shouldQuit, NULL); - attrstr = uiNewAttributedString(text); - mainwin = uiNewWindow("libui Text-Drawing Example", 640, 480, 1); uiWindowOnClosing(mainwin, onClosing, NULL); + box = uiNewVerticalBox(); + uiWindowSetChild(mainwin, uiControl(box)); + + exampleList = uiNewCombobox(); + uiBoxAppend(box, uiControl(exampleList), 0); + area = uiNewArea(&handler); - // TODO on GTK+ this doesn't get expand properties set properly? - uiWindowSetChild(mainwin, uiControl(area)); + uiBoxAppend(box, uiControl(area), 1); + + n = 0; + examples[n] = mkBasicExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiBoxAppend(box, examples[n]->panel, 0); + n++; + // and set things up for the initial state + uiComboboxSetSelected(exampleList, 0); + uiComboboxOnSelected(exampleList, onExampleChanged, NULL); + // and set up the first one + onExampleChanged(NULL, NULL); uiControlShow(uiControl(mainwin)); uiMain(); - uiFreeAttributedString(attrstr); + + // TODO free examples + uiUninit(); return 0; } From 6ccf436206396a2a3b61400d18d00e1df18b74b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 16:36:44 -0500 Subject: [PATCH 059/487] Implemented Pango text metrics and expanded the drawtext basic page to draw metrics. Works on both Pango and OS X; DirectWrite comes next. --- darwin/drawtext.m | 2 + examples/drawtext/basic.c | 187 +++++++++++++++++++++++++++++++++++++- unix/drawtext.c | 60 +++++++++++- 3 files changed, 241 insertions(+), 8 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a8a34542..a38c152d 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -246,6 +246,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) } // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral +// TODO width doesn't include trailing whitespace... +// TODO figure out how paragraph spacing should play into this void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { *width = tl->size.width; diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index 2d3d7f3d..04ea42e1 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -30,10 +30,93 @@ static uiAttributedString *attrstr; #define margins 10 +static uiBox *panel; +static uiCheckbox *showExtents; +static uiCheckbox *showLineBounds; +static uiCheckbox *showLineGuides; + +// TODO should this be const? +static double strokeDashes[] = { 5, 2 }; +// TODO this should be const +static uiDrawStrokeParams strokeParams = { + .Cap = uiDrawLineCapFlat, + .Join = uiDrawLineJoinMiter, + .Thickness = 1, + .MiterLimit = uiDrawDefaultMiterLimit, + .Dashes = strokeDashes, + .NumDashes = 2, + .DashPhase = 0, +}; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; +// TODO come up with better colors +static uiDrawBrush strokeBrushes[3] = { + // baseline + { + .Type = uiDrawBrushTypeSolid, + .R = 0.5, + .G = 0.5, + .B = 0.0, + .A = 0.75, + }, + // ascent + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 1.0, + .A = 0.75, + }, + // descent + { + .Type = uiDrawBrushTypeSolid, + .R = 0.5, + .G = 0.75, + .B = 1.0, + .A = 0.75, + }, +}; + static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; + uiDrawBrush b; + + b.Type = uiDrawBrushTypeSolid; + + // only clip the text, not the guides + uiDrawSave(p->Context); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, margins, margins, @@ -44,33 +127,131 @@ static void draw(uiAreaDrawParams *p) uiDrawFreePath(path); // TODO get rid of this later +#if 0 path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, -100, -100, p->AreaWidth * 2, p->AreaHeight * 2); uiDrawPathEnd(path); - uiDrawBrush b; - b.Type = uiDrawBrushTypeSolid; b.R = 0.0; b.G = 1.0; b.B = 0.0; b.A = 1.0; uiDrawFill(p->Context, path, &b); uiDrawFreePath(path); +#endif layout = uiDrawNewTextLayout(attrstr, &defaultFont, p->AreaWidth - 2 * margins); uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + if (uiCheckboxChecked(showExtents)) { + double width, height; + + uiDrawTextLayoutExtents(layout, &width, &height); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + width, height); + uiDrawPathEnd(path); + b.R = 1.0; + b.G = 0.0; + b.B = 1.0; + b.A = 0.5; + uiDrawStroke(p->Context, path, &b, &strokeParams); + uiDrawFreePath(path); + } + + if (uiCheckboxChecked(showLineBounds) || uiCheckboxChecked(showLineGuides)) { + uiDrawTextLayoutLineMetrics m; + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + + if (uiCheckboxChecked(showLineBounds)) { + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + if (uiCheckboxChecked(showLineGuides)) { + // baseline + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, + margins + m.X, + margins + m.BaselineY); + uiDrawPathLineTo(path, + margins + m.X + m.Width, + margins + m.BaselineY); + uiDrawPathEnd(path); + uiDrawStroke(p->Context, path, &(strokeBrushes[0]), &strokeParams); + uiDrawFreePath(path); + + // ascent line + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, + margins + m.X, + margins + m.BaselineY - m.Ascent); + uiDrawPathLineTo(path, + margins + m.X + m.Width, + margins + m.BaselineY - m.Ascent); + uiDrawPathEnd(path); + uiDrawStroke(p->Context, path, &(strokeBrushes[1]), &strokeParams); + uiDrawFreePath(path); + + // descent line + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, + margins + m.X, + margins + m.BaselineY + m.Descent); + uiDrawPathLineTo(path, + margins + m.X + m.Width, + margins + m.BaselineY + m.Descent); + uiDrawPathEnd(path); + uiDrawStroke(p->Context, path, &(strokeBrushes[2]), &strokeParams); + uiDrawFreePath(path); + } + } + } + uiDrawFreeTextLayout(layout); } static struct example basicExample; +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static uiCheckbox *newCheckbox(const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(panel, uiControl(c), 0); + return c; +} + struct example *mkBasicExample(void) { + panel = uiNewVerticalBox(); + showExtents = newCheckbox("Show Layout Extents"); + showLineBounds = newCheckbox("Show Line Bounds"); + showLineGuides = newCheckbox("Show Line Guides"); + basicExample.name = "Basic Paragraph of Text"; - basicExample.panel = uiControl(uiNewVerticalBox()); + basicExample.panel = uiControl(panel); basicExample.draw = draw; attrstr = uiNewAttributedString(text); diff --git a/unix/drawtext.c b/unix/drawtext.c index 64ae9928..217b3c48 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,6 +4,7 @@ struct uiDrawTextLayout { PangoLayout *layout; + uiDrawTextLayoutLineMetrics *lineMetrics; }; // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description @@ -35,6 +36,55 @@ static const PangoStretch pangoStretches[] = { [uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, }; +// TODO neither these nor the overall extents seem to include trailing whitespace... we need to figure that out too +static void computeLineMetrics(uiDrawTextLayout *tl) +{ + PangoLayoutIter *iter; + PangoLayoutLine *pll; + PangoRectangle lineStartPos, lineExtents; + int i, n; + uiDrawTextLayoutLineMetrics *m; + + n = pango_layout_get_line_count(tl->layout); + tl->lineMetrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + iter = pango_layout_get_iter(tl->layout); + + m = tl->lineMetrics; + for (i = 0; i < n; i++) { + int baselineY; + + // TODO we use this instead of _get_yrange() because of the block of text in that function's description about how line spacing is distributed in Pango; we have to worry about this when we start adding line spacing... + baselineY = pango_layout_iter_get_baseline(iter); + pll = pango_layout_iter_get_line_readonly(iter); + pango_layout_index_to_pos(tl->layout, pll->start_index, &lineStartPos); + pango_layout_line_get_extents(pll, NULL, &lineExtents); + // TODO unref pll? + + // TODO is this correct for RTL glyphs? + m->X = pangoToCairo(lineStartPos.x); + // TODO fix the whole combined not being updated shenanigans in the static build (here because ugh) + m->Y = pangoToCairo(baselineY - PANGO_ASCENT(lineExtents)); + m->Width = pangoToCairo(lineExtents.width); + m->Height = pangoToCairo(lineExtents.height); + + m->BaselineY = pangoToCairo(baselineY); + m->Ascent = pangoToCairo(PANGO_ASCENT(lineExtents)); + m->Descent = pangoToCairo(PANGO_DESCENT(lineExtents)); + m->Leading = 0; // TODO + + m->ParagraphSpacingBefore = 0; // TODO + m->LineHeightSpace = 0; // TODO + m->LineSpacing = 0; // TODO + m->ParagraphSpacing = 0; // TODO + + // don't worry about the return value; we're not using this after the last line + pango_layout_iter_next_line(iter); + m++; + } + + pango_layout_iter_free(iter); +} + uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { uiDrawTextLayout *tl; @@ -76,11 +126,14 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO attributes + computeLineMetrics(tl); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + uiFree(tl->lineMetrics); g_object_unref(tl->layout); uiFree(tl); } @@ -112,15 +165,12 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start pll = pango_layout_get_line_readonly(tl->layout, line); *start = pll->start_index; *end = pll->start_index + pll->length; - // TODO unref? + // TODO unref pll? } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - PangoLayoutLine *pll; - - pll = pango_layout_get_line_readonly(tl->layout, line); - // TODO unref? + *m = tl->lineMetrics[line]; } // TODO From fc7fcd9f0596e05d6fc6c5d79fb3100ebcaab8d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 18:09:06 -0500 Subject: [PATCH 060/487] And implemented metrics stuff on Windows. --- windows/drawtext.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 1e35a582..208f02bc 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -227,6 +227,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) } // TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work... +// TODO width does not include trailing whitespace void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { DWRITE_TEXT_METRICS metrics; @@ -246,6 +247,7 @@ int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) } // DirectWrite doesn't provide a direct way to do this, so we have to do this manually +// TODO does that comment still apply here or to the code at the top of this file? void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { *start = tl->lineInfo[line].startPos; @@ -256,7 +258,21 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - // TODO + m->X = tl->lineInfo[line].x; + m->Y = tl->lineInfo[line].y; + m->Width = tl->lineInfo[line].width; + m->Height = tl->lineInfo[line].height; + + // TODO rename tl->lineInfo[line].baseline to .baselineOffset or something of the sort to make its meaning more clear + m->BaselineY = tl->lineInfo[line].y + tl->lineInfo[line].baseline; + m->Ascent = tl->lineInfo[line].baseline; + m->Descent = tl->lineInfo[line].height - tl->lineInfo[line].baseline; + m->Leading = 0; // TODO + + m->ParagraphSpacingBefore = 0; // TODO + m->LineHeightSpace = 0; // TODO + m->LineSpacing = 0; // TODO + m->ParagraphSpacing = 0; // TODO } void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) From 87b7d5b4b7e9d15e9db1b45a52e1972f948153e3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 19:34:16 -0500 Subject: [PATCH 061/487] Decided to remove uiDrawTextLayoutByteIndexToGraphemeRect(); the Range one later on handles it. Now we can do the hit testing functions! --- darwin/drawtext.m | 4 ---- ui_attrstr.h | 6 +++--- unix/drawtext.c | 4 ---- windows/drawtext.cpp | 4 ---- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a38c152d..f782d614 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -275,10 +275,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } -void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) -{ -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { CFIndex i; diff --git a/ui_attrstr.h b/ui_attrstr.h index 7cfedb10..8ca835ea 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -136,6 +136,9 @@ struct uiDrawTextLayoutHitTestResult { // int InTrailingWhitespace; // TODO? // double XFraction; +// extra TODO? +// double YFraction; +// or just have offsets instead? in addition? }; struct uiDrawTextLayoutByteRangeRectangle { @@ -163,9 +166,6 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou _UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -// TODO redo this? remove it entirely? -_UI_EXTERN void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height); -// TODO partial offset? _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); // TODO draw only a line? diff --git a/unix/drawtext.c b/unix/drawtext.c index 217b3c48..f8ec1e67 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -183,10 +183,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif -void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) -{ -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { } diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 208f02bc..52099e79 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -275,10 +275,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } -void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) -{ -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { // TODO From 339bdfc89b9447126f1ddd53717225f945f2e4b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 20:38:18 -0500 Subject: [PATCH 062/487] Started a hit-test and grapheme boundary test. Looks like we're not quite out of the woods with DirectWrite just yet. --- examples/CMakeLists.txt | 1 + examples/drawtext/drawtext.h | 3 ++ examples/drawtext/hittest.c | 69 ++++++++++++++++++++++++++++++++++++ examples/drawtext/main.c | 6 ++++ 4 files changed, 79 insertions(+) create mode 100644 examples/drawtext/hittest.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 430224d8..3fda1b82 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -33,6 +33,7 @@ endif() _add_example(drawtext drawtext/basic.c + drawtext/hittest.c drawtext/main.c ${_EXAMPLE_RESOURCES_RC} ) diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index adbac19b..ec821eb8 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -15,3 +15,6 @@ extern void redraw(void); // basic.c extern struct example *mkBasicExample(void); + +// hittest.c +extern struct example *mkHitTestExample(void); diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c new file mode 100644 index 00000000..37386082 --- /dev/null +++ b/examples/drawtext/hittest.c @@ -0,0 +1,69 @@ +// 20 january 2017 +#include "drawtext.h" + +static const char *text = + "Each of the glyphs an end user interacts with are called graphemes. " + "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " + "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " + "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " + "That being said: " + "\xC3\x93O\xCC\x81 (combining accents) " + "A\xCC\x81\xE0\xAB\x81 (multiple combining accents) " + "\xE2\x80\xAE#\xE2\x80\xAC (RTL glyph) " + "\xF0\x9F\x92\xBB (non-BMP character) " + "\xF0\x9F\x92\xBB\xCC\x80 (combined non-BMP character) " + ""; +static char fontFamily[] = "Helvetica"; +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 14, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiAttributedString *attrstr; + +#define margins 10 + +static uiBox *panel; + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + + // only clip the text, not the guides + uiDrawSave(p->Context); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + p->AreaWidth - 2 * margins); + uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + uiDrawFreeTextLayout(layout); +} + +static struct example hitTestExample; + +struct example *mkHitTestExample(void) +{ + panel = uiNewVerticalBox(); + + hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; + hitTestExample.panel = uiControl(panel); + hitTestExample.draw = draw; + + attrstr = uiNewAttributedString(text); + + return &hitTestExample; +} diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index ebc9210a..8e5a93ec 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -100,6 +100,12 @@ int main(void) n = 0; examples[n] = mkBasicExample(); uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); + uiBoxAppend(box, examples[n]->panel, 0); + n++; + examples[n] = mkHitTestExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); uiBoxAppend(box, examples[n]->panel, 0); n++; // and set things up for the initial state From c720e8147bf4430362ef186bcc3b4caa0c413fc2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 20:57:20 -0500 Subject: [PATCH 063/487] Figured out what's going on. No clue how to fix it. --- windows/drawtext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 52099e79..ae79b1f2 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -86,8 +86,9 @@ static void computeLineInfo(uiDrawTextLayout *tl) hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, 0, 0, &htm, 1, &unused); + // TODO this happens with the hit test string on the line with the RTL override (presumably the overridden part is its own separate result); see how it affects metrics if (hr == E_NOT_SUFFICIENT_BUFFER) - logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); +;else// logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); if (hr != S_OK) logHRESULT(L"error getting IDWriteTextLayout line rect", hr); // TODO verify htm.textPosition and htm.length? From bbed231324bd252cfb93db90cc67fc8eb00a8f82 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 22:25:37 -0500 Subject: [PATCH 064/487] Fixed up misused Unicode. --- examples/drawtext/hittest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 37386082..3d4f7677 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -8,10 +8,10 @@ static const char *text = "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " "\xC3\x93O\xCC\x81 (combining accents) " - "A\xCC\x81\xE0\xAB\x81 (multiple combining accents) " + "A\xCC\xAA\xEF\xB8\xA0 (multiple combining characters) " "\xE2\x80\xAE#\xE2\x80\xAC (RTL glyph) " "\xF0\x9F\x92\xBB (non-BMP character) " - "\xF0\x9F\x92\xBB\xCC\x80 (combined non-BMP character) " + "\xF0\x9F\x92\xBB\xCC\x80 (combined non-BMP character; may render strangely) " ""; static char fontFamily[] = "Helvetica"; static uiDrawFontDescriptor defaultFont = { From 351b3b607708ca9d2b66db14debde4e2ca6b9b66 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 22:39:37 -0500 Subject: [PATCH 065/487] Added some debugging for that DirectWrite RTL stuff. --- examples/drawtext/hittest.c | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 3d4f7677..dc1f9dce 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -26,6 +26,39 @@ static uiAttributedString *attrstr; #define margins 10 static uiBox *panel; +static uiCheckbox *showLineBounds; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; static void draw(uiAreaDrawParams *p) { @@ -50,14 +83,49 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); + if (uiCheckboxChecked(showLineBounds)) { + uiDrawTextLayoutLineMetrics m; + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + } + uiDrawFreeTextLayout(layout); } static struct example hitTestExample; +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static uiCheckbox *newCheckbox(const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(panel, uiControl(c), 0); + return c; +} + struct example *mkHitTestExample(void) { panel = uiNewVerticalBox(); + showLineBounds = newCheckbox("Show Line Bounds (for debugging metrics)"); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); From 4de8d4402fea5f792922668f78d510f2c97db431 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 23:15:10 -0500 Subject: [PATCH 066/487] More TODOs. --- darwin/drawtext.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f782d614..2ac104c6 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -82,6 +82,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } +// TODO this is wrong for our grapheme test; figure out what it should be... static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; From f0b9ff9aba0df03ce79faa53eb6008237c80467d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 09:39:53 -0500 Subject: [PATCH 067/487] Fixed multifragment lines on Windows, again with the help of the PadWrite sample. --- windows/drawtext.cpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index ae79b1f2..0caac7f3 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -54,9 +54,9 @@ static void computeLineInfo(uiDrawTextLayout *tl) { DWRITE_LINE_METRICS *dlm; size_t nextStart; - UINT32 i; - DWRITE_HIT_TEST_METRICS htm; - UINT32 unused; + UINT32 i, j; + DWRITE_HIT_TEST_METRICS *htm; + UINT32 nFragments, unused; HRESULT hr; // TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead @@ -83,20 +83,34 @@ static void computeLineInfo(uiDrawTextLayout *tl) tl->lineInfo[i].newlineCount = dlm[i].newlineLength; nextStart = tl->lineInfo[i].endPos; + // a line can have multiple fragments; for example, if there's a bidirectional override in the middle of a line hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, 0, 0, - &htm, 1, &unused); - // TODO this happens with the hit test string on the line with the RTL override (presumably the overridden part is its own separate result); see how it affects metrics - if (hr == E_NOT_SUFFICIENT_BUFFER) -;else// logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); + NULL, 0, &nFragments); + if (hr != S_OK && hr != E_NOT_SUFFICIENT_BUFFER) + logHRESULT(L"error getting IDWriteTextLayout line fragment count", hr); + htm = new DWRITE_HIT_TEST_METRICS[nFragments]; + // TODO verify unused == nFragments? + hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, + 0, 0, + htm, nFragments, &unused); + // TODO can this return E_NOT_SUFFICIENT_BUFFER again? if (hr != S_OK) - logHRESULT(L"error getting IDWriteTextLayout line rect", hr); - // TODO verify htm.textPosition and htm.length? - tl->lineInfo[i].x = htm.left; - tl->lineInfo[i].y = htm.top; - tl->lineInfo[i].width = htm.width; - tl->lineInfo[i].height = htm.height; - // TODO verify dlm[i].height == htm.height + logHRESULT(L"error getting IDWriteTextLayout line fragment metrics", hr); + // TODO verify htm.textPosition and htm.length against dtm[i]/tl->lineInfo[i]? + tl->lineInfo[i].x = htm[0].left; + tl->lineInfo[i].y = htm[0].top; + tl->lineInfo[i].width = htm[0].width; + tl->lineInfo[i].height = htm[0].height; + for (j = 1; j < nFragments; j++) { + // this is correct even if the leftmost fragment on the line is RTL + if (tl->lineInfo[i].x > htm[j].left) + tl->lineInfo[i].x = htm[j].left; + tl->lineInfo[i].width += htm[j].width; + // TODO verify y and height haven't changed? + } + // TODO verify dlm[i].height == htm.height? + delete[] htm; // TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative? // TODO and even on those platforms can we somehow split tyographic leading from spacing? From d8316790a00ed1e7838a1f3e10c886c70f084bf1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 10:21:39 -0500 Subject: [PATCH 068/487] More notes. --- darwin/drawtext.m | 2 +- unix/drawtext.c | 3 +++ windows/drawtext.cpp | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 2ac104c6..a84b68b5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -82,7 +82,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } -// TODO this is wrong for our grapheme test; figure out what it should be... +// TODO this is wrong for our hit-test example's multiple combining character example static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; diff --git a/unix/drawtext.c b/unix/drawtext.c index f8ec1e67..6c01bc7d 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -2,6 +2,9 @@ #include "uipriv_unix.h" #include "draw.h" +// TODO +// - if the RTL override is at the beginning of a line, the preceding space is included? + struct uiDrawTextLayout { PangoLayout *layout; uiDrawTextLayoutLineMetrics *lineMetrics; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 0caac7f3..345723ce 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -2,6 +2,10 @@ #include "uipriv_windows.hpp" #include "draw.hpp" +// TODO +// - consider the warnings about antialiasing in the PadWrite sample +// - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... + struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; From 313905230da987e00ba56f2f9c283fd0e8994392 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 15:22:19 -0500 Subject: [PATCH 069/487] Ugh, Core Text and NSLayoutManager produce inconsistent output :| --- stats.osxdrawtext | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 stats.osxdrawtext diff --git a/stats.osxdrawtext b/stats.osxdrawtext new file mode 100644 index 00000000..15c9c6c5 --- /dev/null +++ b/stats.osxdrawtext @@ -0,0 +1,43 @@ +diff --git a/darwin/drawtext.m b/darwin/drawtext.m +index a84b68b..c95bbde 100644 +--- a/darwin/drawtext.m ++++ b/darwin/drawtext.m +@@ -108,7 +108,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr + boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); + + // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) +- ascent = bounds.size.height + bounds.origin.y; ++if(i!=5) ascent = bounds.size.height + bounds.origin.y; + descent = -boundsNoLeading.origin.y; + // TODO does this preserve leading sign? + leading = -bounds.origin.y - descent; +@@ -119,11 +119,20 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr + if (leading > 0) + leading = floor(leading + 0.5); + ++NSLog(@"line %d", (int)i); ++NSLog(@"ypos %g", ypos); ++if (i>0) { ++NSLog(@"expected Y: %g", metrics[i - 1].Y - metrics[i - 1].Height); ++} ++ + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; + metrics[i].Height = ascent + descent + leading; + ++NSLog(@"o %g a %g d %g l %g", origins[i].y, ascent, descent, leading); ++NSLog(@"actual Y: %g height: %g", metrics[i].Y, metrics[i].Height); ++ + metrics[i].BaselineY = origins[i].y; + metrics[i].Ascent = ascent; + metrics[i].Descent = descent; +@@ -148,7 +157,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr + metrics[i].BaselineY = size.height - metrics[i].BaselineY; + // TODO also adjust by metrics[i].Height? + } +- ++NSLog(@"==="); + uiFree(origins); + return metrics; + } From 7eba767ffb26e91911078813b924671531b5e79b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 18:27:03 -0500 Subject: [PATCH 070/487] Temporarily turning off excess text for magnification tests. --- examples/drawtext/hittest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index dc1f9dce..a2454549 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,8 +3,8 @@ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " - "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " - "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " +//TODO "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " +//TODO "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " "\xC3\x93O\xCC\x81 (combining accents) " From 4e2dc90f4f202dbbfdccdfb428a70f63f463b518 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 01:28:53 -0500 Subject: [PATCH 071/487] Started an experimental port from Core Text to Cocoa's typesetting system, since that seems to produce more sensible results (and is somewhat easier to use...). We lose per-line spacing though :/ --- darwin/_coretext_drawtext.m | 268 +++++++++++++++++++++++++++++++++++ darwin/_coretext_fontmatch.m | 255 +++++++++++++++++++++++++++++++++ darwin/fontmatch.m | 131 +++++++---------- darwin/uipriv_darwin.h | 3 +- 4 files changed, 576 insertions(+), 81 deletions(-) create mode 100644 darwin/_coretext_drawtext.m create mode 100644 darwin/_coretext_fontmatch.m diff --git a/darwin/_coretext_drawtext.m b/darwin/_coretext_drawtext.m new file mode 100644 index 00000000..72a8d2e6 --- /dev/null +++ b/darwin/_coretext_drawtext.m @@ -0,0 +1,268 @@ +// 6 september 2015 +#import "uipriv_darwin.h" + +// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) +struct uiDrawFontFamilies { + CFArrayRef fonts; +}; + +uiDrawFontFamilies *uiDrawListFontFamilies(void) +{ + uiDrawFontFamilies *ff; + + ff = uiNew(uiDrawFontFamilies); + ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); + if (ff->fonts == NULL) + implbug("error getting available font names (no reason specified) (TODO)"); + return ff; +} + +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +{ + return CFArrayGetCount(ff->fonts); +} + +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) +{ + CFStringRef familystr; + char *family; + + familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); + // toll-free bridge + family = uiDarwinNSStringToText((NSString *) familystr); + // Get Rule means we do not free familystr + return family; +} + +void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) +{ + CFRelease(ff->fonts); + uiFree(ff); +} + +struct uiDrawTextFont { + CTFontRef f; +}; + +uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) +{ + uiDrawTextFont *font; + + font = uiNew(uiDrawTextFont); + font->f = f; + if (retain) + CFRetain(font->f); + return font; +} + +uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) +{ + // toll-free bridging; we do retain, though + return mkTextFont((CTFontRef) f, YES); +} + +static CFMutableDictionaryRef newAttrList(void) +{ + CFMutableDictionaryRef attr; + + attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (attr == NULL) + complain("error creating attribute dictionary in newAttrList()()"); + return attr; +} + +static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) +{ + CFStringRef cfstr; + + cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); + if (cfstr == NULL) + complain("error creating font family name CFStringRef in addFontFamilyAttr()"); + CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); + CFRelease(cfstr); // dictionary holds its own reference +} + +static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) +{ + CFNumberRef n; + + n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); + CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); + CFRelease(n); +} + +#if 0 +TODO +// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do +// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D +static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) +{ + CFMutableArrayRef outerArray; + CFMutableDictionaryRef innerDict; + CFNumberRef numType, numSelector; + int num; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) + complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); + + // Apple's headers say these are deprecated, but a few fonts still rely on them + num = kLetterCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + // these are the non-deprecated versions of the above; some fonts have these instead + num = kLowerCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kLowerCaseSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); + CFRelease(outerArray); +} +#endif + +#if 0 +// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( +// kode54 got these for me before I had access to El Capitan; thanks to him. +#define ourNSFontWeightUltraLight -0.800000 +#define ourNSFontWeightThin -0.600000 +#define ourNSFontWeightLight -0.400000 +#define ourNSFontWeightRegular 0.000000 +#define ourNSFontWeightMedium 0.230000 +#define ourNSFontWeightSemibold 0.300000 +#define ourNSFontWeightBold 0.400000 +#define ourNSFontWeightHeavy 0.560000 +#define ourNSFontWeightBlack 0.620000 +#endif + +// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. +CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) +{ + CFDictionaryRef dict; + CFMutableDictionaryRef mdict; + + dict = CTFontDescriptorCopyAttributes(desc); + // this might not be mutable, so make a mutable copy + mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); + CFRelease(dict); + return mdict; +} + +uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +{ + CTFontRef f; + CFMutableDictionaryRef attr; + CTFontDescriptorRef cfdesc; + + attr = newAttrList(); + addFontFamilyAttr(attr, desc->Family); + addFontSizeAttr(attr, desc->Size); + + // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back + cfdesc = CTFontDescriptorCreateWithAttributes(attr); + // TODO release attr? + cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); + + // specify the initial size again just to be safe + f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); + // TODO release cfdesc? + + return mkTextFont(f, NO); // we hold the initial reference; no need to retain again +} + +void uiDrawFreeTextFont(uiDrawTextFont *font) +{ + CFRelease(font->f); + uiFree(font); +} + +uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +{ + return (uintptr_t) (font->f); +} + +void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +{ + // TODO +} + +// text sizes and user space points are identical: +// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch +// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch +void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +{ + metrics->Ascent = CTFontGetAscent(font->f); + metrics->Descent = CTFontGetDescent(font->f); + metrics->Leading = CTFontGetLeading(font->f); + metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); + metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); +} + +// LONGTERM allow line separation and leading to be factored into a wrapping text layout + +// TODO reconcile differences in character wrapping on platforms +void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +{ + struct framesetter fs; + + mkFramesetter(layout, &fs); + *width = fs.extents.width; + *height = fs.extents.height; + freeFramesetter(&fs); +} + +// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? + +// LONGTERM keep this for later features and documentation purposes +#if 0 + + // LONGTERM provide a way to get the image bounds as a separate function later + bounds = CTLineGetImageBounds(line, c); + // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error + + // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead + CTLineGetTypographicBounds(line, &yoff, NULL, NULL); + // remember that we're flipped, so we subtract + y -= yoff; + CGContextSetTextPosition(c, x, y); +#endif + +#if 0 +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +{ + CGColorSpaceRef colorspace; + CGFloat components[4]; + CGColorRef color; + + // for consistency with windows, use sRGB + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = a; + color = CGColorCreate(colorspace, components); + CGColorSpaceRelease(colorspace); + + CFAttributedStringSetAttribute(layout->mas, + rangeToCFRange(), + kCTForegroundColorAttributeName, + color); + CGColorRelease(color); // TODO safe? +} +#endif diff --git a/darwin/_coretext_fontmatch.m b/darwin/_coretext_fontmatch.m new file mode 100644 index 00000000..04761ef4 --- /dev/null +++ b/darwin/_coretext_fontmatch.m @@ -0,0 +1,255 @@ +// 3 january 2017 +#import "uipriv_darwin.h" + +// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. +// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for +// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) +// - CTFontCreateWithFontDescriptor() +// - CTFontCreateCopyWithAttributes() +// We have to implement the closest match ourselves. +// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. + +struct closeness { + CFIndex index; + double weight; + double italic; + double stretch; + double distance; +}; + +static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) +{ + CFNumberRef cfnum; + double val; + + cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { + // TODO + } + // Get Rule; do not release cfnum + return val; +} + +struct italicCloseness { + double normal; + double oblique; + double italic; +}; + +// remember that in closeness, 0 means exact +// in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match" +static const struct italicCloseness italicClosenesses[] = { + [uiDrawTextItalicNormal] = { 0, 1, 1 }, + [uiDrawTextItalicOblique] = { 1, 0, 0.5 }, + [uiDrawTextItalicItalic] = { 1, 0.5, 0 }, +}; + +// Italics are hard because Core Text does NOT distinguish between italic and oblique. +// All Core Text provides is a slant value and the italic bit of the symbolic traits mask. +// However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. +// Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) +// TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) +static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +{ + const struct italicCloseness *ic = &(italicClosenesses[italic]); + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work + SInt32 s; + CFStringRef styleName; + BOOL isOblique; + + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { + // TODO + } + symbolic = (CTFontSymbolicTraits) s; + // Get Rule; do not release cfnum + if ((symbolic & kCTFontItalicTrait) == 0) + return ic->normal; + + // Okay, now we know it's either Italic or Oblique + // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + // TODO is styleName guaranteed? + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return ic->oblique; + return ic->italic; +} + +static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +{ + CFArrayRef matching; + CFIndex i, n; + struct closeness *closeness; + CTFontDescriptorRef current; + CTFontDescriptorRef out; + + matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); + if (matching == NULL) + // no matches; give the original back and hope for the best + return against; + n = CFArrayGetCount(matching); + if (n == 0) { + // likewise + CFRelease(matching); + return against; + } + + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + for (i = 0; i < n; i++) { + CFDictionaryRef traits; + + closeness[i].index = i; + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); + if (traits == NULL) { + // couldn't get traits; be safe by ranking it lowest + // LONGTERM figure out what the longest possible distances are + closeness[i].weight = 3; + closeness[i].italic = 2; + closeness[i].stretch = 3; + continue; + } + closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; + closeness[i].italic = italicCloseness(current, traits, targetItalic); + closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; + CFRelease(traits); + } + + // now figure out the 3-space difference between the three and sort by that + // TODO merge this loop with the previous loop? + for (i = 0; i < n; i++) { + double weight, italic, stretch; + + weight = closeness[i].weight; + weight *= weight; + italic = closeness[i].italic; + italic *= italic; + stretch = closeness[i].stretch; + stretch *= stretch; + closeness[i].distance = sqrt(weight + italic + stretch); + } + qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { + const struct closeness *a = (const struct closeness *) aa; + const struct closeness *b = (const struct closeness *) bb; + + // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions + // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? + return (a->distance > b->distance) - (a->distance < b->distance); + }); + // and the first element of the sorted array is what we want + out = CFArrayGetValueAtIndex(matching, closeness[0].index); + CFRetain(out); // get rule + + // release everything + uiFree(closeness); + CFRelease(matching); + // and release the original descriptor since we no longer need it + CFRelease(against); + + return out; +} + +// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights +// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these +// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) +static const double weightsToCTWeights[] = { + -1.0, // 0..99 + -0.7, // 100..199 + -0.5, // 200..299 + -0.23, // 300..399 + 0.0, // 400..499 + 0.2, // 500..599 + 0.3, // 600..699 + 0.4, // 700..799 + 0.6, // 800..899 + 0.8, // 900..999 + 1.0, // 1000 +}; + +static double weightToCTWeight(uiDrawTextWeight weight) +{ + int weightClass; + double ctclass; + double rest, weightFloor, nextFloor; + + if (weight <= 0) + return -1.0; + if (weight >= 1000) + return 1.0; + + weightClass = weight / 100; + rest = (double) weight; + weightFloor = (double) (weightClass * 100); + nextFloor = (double) ((weightClass + 1) * 100); + rest = (rest - weightFloor) / (nextFloor - weightFloor); + + ctclass = weightsToCTWeights[weightClass]; + return fma(rest, + weightsToCTWeights[weightClass + 1] - ctclass, + ctclass); +} + +// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) +static const double stretchesToCTWidths[] = { + [uiDrawTextStretchUltraCondensed] = -0.400000, + [uiDrawTextStretchExtraCondensed] = -0.300000, + [uiDrawTextStretchCondensed] = -0.200000, + [uiDrawTextStretchSemiCondensed] = -0.100000, + [uiDrawTextStretchNormal] = 0.000000, + [uiDrawTextStretchSemiExpanded] = 0.100000, + [uiDrawTextStretchExpanded] = 0.200000, + [uiDrawTextStretchExtraExpanded] = 0.300000, + // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) + [uiDrawTextStretchUltraExpanded] = 0.400000, +}; + +CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef attrs; + CFStringRef cffamily; + CFNumberRef cfsize; + CTFontDescriptorRef basedesc; + + attrs = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (attrs == NULL) { + // TODO + } + cffamily = CFStringCreateWithCString(NULL, fd->Family, kCFStringEncodingUTF8); + if (cffamily == NULL) { + // TODO + } + CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); + CFRelease(cffamily); + cfsize = CFNumberCreate(NULL, kCFNumberDoubleType, &(fd->Size)); + CFDictionaryAddValue(attrs, kCTFontSizeAttribute, cfsize); + CFRelease(cfsize); + + basedesc = CTFontDescriptorCreateWithAttributes(attrs); + CFRelease(attrs); // TODO correct? + return matchTraits(basedesc, + weightToCTWeight(fd->Weight), + fd->Italic, + stretchesToCTWidths[fd->Stretch]); +} diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 04761ef4..27440841 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -6,31 +6,25 @@ // - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) // - CTFontCreateWithFontDescriptor() // - CTFontCreateCopyWithAttributes() +// And as a bonus prize, this also applies to Cocoa's NSFontDescriptor methods as well! // We have to implement the closest match ourselves. // This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. struct closeness { - CFIndex index; + NSUInteger index; double weight; double italic; double stretch; double distance; }; -static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) +static double doubleAttr(NSDictionary *traits, NSString *attr) { - CFNumberRef cfnum; + NSNumber *n; double val; - cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { - // TODO - } - // Get Rule; do not release cfnum - return val; + n = (NSNumber *) [traits objectForKey:attr]; + return [n doubleValue]; } struct italicCloseness { @@ -52,75 +46,62 @@ static const struct italicCloseness italicClosenesses[] = { // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +// TODO see if the above applies to Cocoa as well +static double italicCloseness(NSFontDescriptor *desc, NSDictionary *traits, uiDrawTextItalic italic) { const struct italicCloseness *ic = &(italicClosenesses[italic]); - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work - SInt32 s; - CFStringRef styleName; + NSNumber *num; + NSFontSymbolicTraits symbolic; + NSString *styleName; + NSRange range; BOOL isOblique; - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { - // TODO - } - symbolic = (CTFontSymbolicTraits) s; - // Get Rule; do not release cfnum - if ((symbolic & kCTFontItalicTrait) == 0) + num = (NSNumber *) [traits objectForKey:NSFontSymbolicTrait]; + // TODO this should really be a uint32_t-specific one + symbolic = (NSFontSymbolicTraits) [num unsignedIntegerValue]; + if ((symbolic & NSFontItalicTrait) == 0) return ic->normal; // Okay, now we know it's either Italic or Oblique // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + // note that NSFontFaceAttribute is the Cocoa equivalent of the style name isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + styleName = (NSString *) [desc objectForKey:NSFontFaceAttribute]; // TODO is styleName guaranteed? - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) + // TODO NSLiteralSearch? + range = [styleName rangeOfString:@"Oblique" options:NSCaseInsensitiveSearch]; + if (range.location != NSNotFound) return ic->oblique; return ic->italic; } -static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) { - CFArrayRef matching; - CFIndex i, n; + NSArray *matching; + NSUInteger i, n; struct closeness *closeness; - CTFontDescriptorRef current; - CTFontDescriptorRef out; + NSFontDescriptor *current; + NSFontDescriptor *out; - matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); - if (matching == NULL) + matching = [against matchingFontDescriptorsWithMandatoryKeys:nil]; + if (matching == nil) // no matches; give the original back and hope for the best return against; - n = CFArrayGetCount(matching); + n = [matching count]; if (n == 0) { // likewise - CFRelease(matching); + [matching release]; return against; } closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { - CFDictionaryRef traits; + NSDictionary *traits; closeness[i].index = i; - current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { + current = (NSFontDescriptor *) [matching objectAtIndex:i]; + traits = (NSDictionary *) [current objectAtIndex:NSFontTraitsAttribute]; + if (traits == nil) { // couldn't get traits; be safe by ranking it lowest // LONGTERM figure out what the longest possible distances are closeness[i].weight = 3; @@ -128,10 +109,10 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe closeness[i].stretch = 3; continue; } - closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; + closeness[i].weight = doubleAttr(traits, NSFontWeightTrait) - targetWeight; closeness[i].italic = italicCloseness(current, traits, targetItalic); - closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; - CFRelease(traits); + closeness[i].stretch = doubleAttr(traits, NSFontWidthTrait) - targetStretch; + // TODO release traits? } // now figure out the 3-space difference between the three and sort by that @@ -156,14 +137,15 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe return (a->distance > b->distance) - (a->distance < b->distance); }); // and the first element of the sorted array is what we want - out = CFArrayGetValueAtIndex(matching, closeness[0].index); - CFRetain(out); // get rule + out = (NSFontDescriptor *) [matching objectAtIndex:closeness[0].index]; + // TODO is this correct? + [out retain]; // get rule // release everything uiFree(closeness); - CFRelease(matching); + [matching release]; // and release the original descriptor since we no longer need it - CFRelease(against); + [against release]; return out; } @@ -222,32 +204,21 @@ static const double stretchesToCTWidths[] = { [uiDrawTextStretchUltraExpanded] = 0.400000, }; -CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd) { - CFMutableDictionaryRef attrs; + NSMutableDictionary *attrs; CFStringRef cffamily; CFNumberRef cfsize; CTFontDescriptorRef basedesc; - attrs = CFDictionaryCreateMutable(NULL, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (attrs == NULL) { - // TODO - } - cffamily = CFStringCreateWithCString(NULL, fd->Family, kCFStringEncodingUTF8); - if (cffamily == NULL) { - // TODO - } - CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); - CFRelease(cffamily); - cfsize = CFNumberCreate(NULL, kCFNumberDoubleType, &(fd->Size)); - CFDictionaryAddValue(attrs, kCTFontSizeAttribute, cfsize); - CFRelease(cfsize); + attrs = [NSMutableDictionary new]; + [attrs setObject:[NSString stringWithUTF8String:fd->Family] + forKey:NSFontFamilyAttribute]; + [attrs setObject:[NSNumber numberWithDouble:fd->Size] + forKye:NSFontSizeAttribute]; - basedesc = CTFontDescriptorCreateWithAttributes(attrs); - CFRelease(attrs); // TODO correct? + basedesc = [[NSFontDescriptor alloc] initWithFontAttributes:attrs]; + [attrs release]; return matchTraits(basedesc, weightToCTWeight(fd->Weight), fd->Italic, diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c1fde75a..bfbf0688 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -147,4 +147,5 @@ extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); // fontmatch.m -extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +//TODO extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd); From b19f4cf251c6424e95e141ccc87f98b2140bca01 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 11:43:03 -0500 Subject: [PATCH 072/487] Finished writing a NSLayoutManager-based text system. Not quite perfect yet, but we're getting somewhere! --- darwin/drawtext.m | 315 ++++++++++++++++++--------------------------- darwin/fontmatch.m | 13 +- 2 files changed, 133 insertions(+), 195 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a84b68b5..6a16af1e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -2,16 +2,26 @@ #import "uipriv_darwin.h" #import "draw.h" -// TODO what happens if nLines == 0 in any function? +@interface lineInfo : NSObject +@property NSRange glyphRange; +@property NSRange characterRange; +@property NSRect lineRect; +@property CGFloat baselineOffset; +@end + +@implementation lineInfo +@end struct uiDrawTextLayout { - CFAttributedStringRef attrstr; + // NSTextStorage is subclassed from NSMutableAttributedString + NSTextStorage *attrstr; + NSTextContainer *container; + NSLayoutManager *layoutManager; // the width as passed into uiDrawTextLayout constructors double width; - CTFramesetterRef framesetter; - +#if 0 /* TODO */ // the *actual* size of the frame // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path @@ -19,188 +29,101 @@ struct uiDrawTextLayout { // so we can just use tl->size for adjustments // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) CGSize size; +#endif - CGPathRef path; - CTFrameRef frame; - - CFArrayRef lines; - CFIndex nLines; - // we compute this once when first creating the layout - uiDrawTextLayoutLineMetrics *lineMetrics; + NSMutableArray *lineInfo; // for converting CFAttributedString indices to byte offsets size_t *u16tou8; size_t nu16tou8; // TODO I don't like the casing of this name }; -static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +static NSFont *fontdescToNSFont(uiDrawFontDescriptor *fd) { - CTFontDescriptorRef desc; - CTFontRef font; + NSFontDescriptor *desc; + NSFont *font; - desc = fontdescToCTFontDescriptor(fd); - font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); - CFRelease(desc); // TODO correct? + desc = fontdescToNSFontDescriptor(fd); + font = [NSFont fontWithDescriptor:desc size:fd->Size]; + [desc release]; return font; } -static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) +static NSTextStorage *attrstrToTextStorage(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { - CFStringRef cfstr; - CFMutableDictionaryRef defaultAttrs; - CTFontRef defaultCTFont; - CFAttributedStringRef base; - CFMutableAttributedStringRef mas; + NSString *nsstr; + NSMutableDictionary *defaultAttrs; + NSTextStorage *attrstr; - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); - if (cfstr == NULL) { - // TODO - } - defaultAttrs = CFDictionaryCreateMutable(NULL, 1, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (defaultAttrs == NULL) { - // TODO - } - defaultCTFont = fontdescToCTFont(defaultFont); - CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); - CFRelease(defaultCTFont); + nsstr = [[NSString alloc] initWithCharacters:attrstrUTF16(s) + length:attrstrUTF16Len(s)]; - base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); - if (base == NULL) { - // TODO - } - CFRelease(cfstr); - CFRelease(defaultAttrs); - mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); - CFRelease(base); + defaultAttrs = [NSMutableDictionary new]; + [defaultAttrs setObject:fontdescToNSFont(defaultFont) + forKey:NSFontAttributeName]; - CFAttributedStringBeginEditing(mas); + attrstr = [[NSTextStorage alloc] initWithString:nsstr + attributes:defaultAttrs]; + [defaultAttrs release]; + [nsstr release]; + + [attrstr beginEditing]; // TODO copy in the attributes - CFAttributedStringEndEditing(mas); + [attrstr endEditing]; - return mas; -} - -// TODO this is wrong for our hit-test example's multiple combining character example -static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) -{ - uiDrawTextLayoutLineMetrics *metrics; - CFArrayRef lines; - CTLineRef line; - CFIndex i, n; - CGFloat ypos; - CGRect bounds, boundsNoLeading; - CGFloat ascent, descent, leading; - CGPoint *origins; - - lines = CTFrameGetLines(frame); - n = CFArrayGetCount(lines); - metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); - - origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)"); - CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); - - ypos = size.height; - for (i = 0; i < n; i++) { - line = (CTLineRef) CFArrayGetValueAtIndex(lines, i); - bounds = CTLineGetBoundsWithOptions(line, 0); - boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); - - // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) - ascent = bounds.size.height + bounds.origin.y; - descent = -boundsNoLeading.origin.y; - // TODO does this preserve leading sign? - leading = -bounds.origin.y - descent; - - // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) - ascent = floor(ascent + 0.5); - descent = floor(descent + 0.5); - if (leading > 0) - leading = floor(leading + 0.5); - - metrics[i].X = origins[i].x; - metrics[i].Y = origins[i].y - descent - leading; - metrics[i].Width = bounds.size.width; - metrics[i].Height = ascent + descent + leading; - - metrics[i].BaselineY = origins[i].y; - metrics[i].Ascent = ascent; - metrics[i].Descent = descent; - metrics[i].Leading = leading; - - // TODO - metrics[i].ParagraphSpacingBefore = 0; - metrics[i].LineHeightSpace = 0; - metrics[i].LineSpacing = 0; - metrics[i].ParagraphSpacing = 0; - - // and finally advance to the next line - ypos += metrics[i].Height; - } - - // okay, but now all these metrics are unflipped - // we need to flip them - for (i = 0; i < n; i++) { - metrics[i].Y = size.height - metrics[i].Y; - // go from bottom-left corner to top-left - metrics[i].Y -= metrics[i].Height; - metrics[i].BaselineY = size.height - metrics[i].BaselineY; - // TODO also adjust by metrics[i].Height? - } - - uiFree(origins); - return metrics; + return attrstr; } +// TODO fine-tune all the properties uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { uiDrawTextLayout *tl; CGFloat cgwidth; - CFRange range, unused; - CGRect rect; + // TODO correct type? + NSUInteger index; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(s, defaultFont); - range.location = 0; - range.length = CFAttributedStringGetLength(tl->attrstr); + tl->attrstr = attrstrToTextStorage(s, defaultFont); tl->width = width; - // TODO CTFrameProgression for RTL/LTR - // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing - tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); - if (tl->framesetter == NULL) { - // TODO - } - + // TODO the documentation on the size property implies this might not be necessary? cgwidth = (CGFloat) width; if (cgwidth < 0) cgwidth = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - // TODO double check to make sure this TODO was right - tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, - range, - // TODO kCTFramePathWidthAttributeName? - NULL, - CGSizeMake(cgwidth, CGFLOAT_MAX), - &unused); // not documented as accepting NULL (TODO really?) + // TODO rename to tl->textContainer + tl->container = [[NSTextContainer alloc] initWithSize:NSMakeSize(cgwidth, CGFLOAT_MAX)]; + // TODO pull the reference for this + [tl->container setLineFragmentPadding:0]; - rect.origin = CGPointZero; - rect.size = tl->size; - tl->path = CGPathCreateWithRect(rect, NULL); - tl->frame = CTFramesetterCreateFrame(tl->framesetter, - range, - tl->path, - // TODO kCTFramePathWidthAttributeName? - NULL); - if (tl->frame == NULL) { - // TODO + tl->layoutManager = [[NSLayoutManager alloc] init]; + + [tl->layoutManager addTextContainer:tl->container]; + [tl->attrstr addLayoutManager:tl->layoutManager]; + // and force a re-layout (TODO get source + [tl->layoutManager glyphRangeForTextContainer:tl->container]; + + // TODO equivalent of CTFrameProgression for RTL/LTR? + + // now collect line information; see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html + tl->lineInfo = [NSMutableArray new]; + index = 0; + while (index < [tl->layoutManager numberOfGlyphs]) { + NSRange glyphRange; + NSRect lineRect; + lineInfo *li; + + lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange]; + li = [lineInfo new]; + li.glyphRange = glyphRange; + li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL]; + li.lineRect = lineRect; + // and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html + li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index]; + [tl->lineInfo addObject:li]; + [li release]; + index = glyphRange.location + glyphRange.length; } - tl->lines = CTFrameGetLines(tl->frame); - tl->nLines = CFArrayGetCount(tl->lines); - tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); - // and finally copy the UTF-16 to UTF-8 index conversion table tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); @@ -210,74 +133,91 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); - uiFree(tl->lineMetrics); - // TODO release tl->lines? - CFRelease(tl->frame); - CFRelease(tl->path); - CFRelease(tl->framesetter); - CFRelease(tl->attrstr); + [tl->lineInfo release]; + [tl->layoutManager release]; + [tl->container release]; + [tl->attrstr release]; uiFree(tl); } // TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - CGContextSaveGState(c->c); + NSGraphicsContext *gc; - // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped - // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) - // TODO how is this affected by a non-identity CTM? - CGContextTranslateCTM(c->c, 0, c->height); - CGContextScaleCTM(c->c, 1.0, -1.0); - CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); + CGContextFlush(c->c); // just to be safe + [NSGraphicsContext saveGraphicsState]; + gc = [NSGraphicsContext graphicsContextWithGraphicsPort:c->c flipped:YES]; + [NSGraphicsContext setCurrentContext:gc]; - // wait, that's not enough; we need to offset y values to account for our new flipping - // TODO explain this calculation - y = c->height - tl->size.height - y; + // TODO is this the right point? + // TODO does this draw with the proper default styles? + [tl->layoutManager drawGlyphsForGlyphRange:[tl->layoutManager glyphRangeForTextContainer:tl->container] + atPoint:NSMakePoint(x, y)]; - // CTFrameDraw() draws in the path we specified when creating the frame - // this means that in our usage, CTFrameDraw() will draw at (0,0) - // so move the origin to be at (x,y) instead - // TODO are the signs correct? - CGContextTranslateCTM(c->c, x, y); - - CTFrameDraw(tl->frame, c->c); - - CGContextRestoreGState(c->c); + [gc flushGraphics]; // just to be safe + [NSGraphicsContext restoreGraphicsState]; + // TODO release gc? } +// TODO update all of these { // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral // TODO width doesn't include trailing whitespace... // TODO figure out how paragraph spacing should play into this +// } void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - *width = tl->size.width; - *height = tl->size.height; + NSRect r; + + // see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html + r = [tl->layoutManager usedRectForTextContainer:tl->container]; + *width = r.size.width; + *height = r.size.height; } int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - return tl->nLines; + return [tl->lineInfo count]; } void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - CTLineRef lr; - CFRange range; + lineInfo *li; - lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); - range = CTLineGetStringRange(lr); - *start = tl->u16tou8[range.location]; - *end = tl->u16tou8[range.location + range.length]; + li = (lineInfo *) [tl->lineInfo objectAtIndex:line]; + *start = tl->u16tou8[li.characterRange.location]; + *end = tl->u16tou8[li.characterRange.location + li.characterRange.length]; } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - *m = tl->lineMetrics[line]; + lineInfo *li; + + li = (lineInfo *) [tl->lineInfo objectAtIndex:line]; + + m->X = li.lineRect.origin.x; + m->Y = li.lineRect.origin.y; + m->Width = li.lineRect.size.width; + m->Height = li.lineRect.size.height; + + // TODO is this correct? + m->BaselineY = (m->X + m->Height) - li.baselineOffset; + + // TODO + m->Ascent = 10000; + m->Descent = 10000; + m->Leading = 10000; + + // TODO + m->ParagraphSpacingBefore = 0; + m->LineHeightSpace = 0; + m->LineSpacing = 0; + m->ParagraphSpacing = 0; } void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { +#if 0 /* TODO */ CFIndex i; CTLineRef line; CFIndex pos; @@ -320,6 +260,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex // TODO } result->Pos = tl->u16tou8[pos]; +#endif } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 27440841..8e81400f 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -21,7 +21,6 @@ struct closeness { static double doubleAttr(NSDictionary *traits, NSString *attr) { NSNumber *n; - double val; n = (NSNumber *) [traits objectForKey:attr]; return [n doubleValue]; @@ -90,7 +89,7 @@ static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWei n = [matching count]; if (n == 0) { // likewise - [matching release]; +//TODO [matching release]; return against; } @@ -100,7 +99,7 @@ static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWei closeness[i].index = i; current = (NSFontDescriptor *) [matching objectAtIndex:i]; - traits = (NSDictionary *) [current objectAtIndex:NSFontTraitsAttribute]; + traits = (NSDictionary *) [current objectForKey:NSFontTraitsAttribute]; if (traits == nil) { // couldn't get traits; be safe by ranking it lowest // LONGTERM figure out what the longest possible distances are @@ -143,7 +142,7 @@ static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWei // release everything uiFree(closeness); - [matching release]; +//TODO [matching release]; // and release the original descriptor since we no longer need it [against release]; @@ -207,15 +206,13 @@ static const double stretchesToCTWidths[] = { NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd) { NSMutableDictionary *attrs; - CFStringRef cffamily; - CFNumberRef cfsize; - CTFontDescriptorRef basedesc; + NSFontDescriptor *basedesc; attrs = [NSMutableDictionary new]; [attrs setObject:[NSString stringWithUTF8String:fd->Family] forKey:NSFontFamilyAttribute]; [attrs setObject:[NSNumber numberWithDouble:fd->Size] - forKye:NSFontSizeAttribute]; + forKey:NSFontSizeAttribute]; basedesc = [[NSFontDescriptor alloc] initWithFontAttributes:attrs]; [attrs release]; From 08f2085f418907ece4fda1123eb59a5bda3534d8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 14:31:39 -0500 Subject: [PATCH 073/487] Quick fix. --- darwin/drawtext.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 6a16af1e..08f37ed8 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -201,7 +201,7 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->Height = li.lineRect.size.height; // TODO is this correct? - m->BaselineY = (m->X + m->Height) - li.baselineOffset; + m->BaselineY = (m->Y + m->Height) - li.baselineOffset; // TODO m->Ascent = 10000; From 1ca9a28f2d42aa2e45fadb10c7e249f1234bd0ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 15:07:28 -0500 Subject: [PATCH 074/487] More work. --- darwin/drawtext.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 08f37ed8..69166610 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -6,6 +6,8 @@ @property NSRange glyphRange; @property NSRange characterRange; @property NSRect lineRect; +@property NSRect lineUsedRect; +@property NSRect glyphBoundingRect; @property CGFloat baselineOffset; @end @@ -117,9 +119,15 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto li.glyphRange = glyphRange; li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL]; li.lineRect = lineRect; + li.lineUsedRect = [tl->layoutManager lineFragmentUsedRectForGlyphAtIndex:index effectiveRange:NULL]; + li.glyphBoundingRect = [tl->layoutManager boundingRectForGlyphRange:li.glyphRange inTextContainer:tl->container]; // and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index]; [tl->lineInfo addObject:li]; +NSLog(@"line %d", (int)[tl->lineInfo count]); +NSLog(@" rect %@", NSStringFromRect(li.lineRect)); +NSLog(@" used rect %@", NSStringFromRect(li.lineUsedRect)); +NSLog(@"glyph rect %@", NSStringFromRect(li.glyphBoundingRect)); [li release]; index = glyphRange.location + glyphRange.length; } @@ -197,7 +205,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->X = li.lineRect.origin.x; m->Y = li.lineRect.origin.y; - m->Width = li.lineRect.size.width; + // if we use li.lineRect here we get the whole line, not just the part with stuff in it + m->Width = li.lineUsedRect.size.width; m->Height = li.lineRect.size.height; // TODO is this correct? From d1e2b17f6e4a793f7610bae72606a5109f8629f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 01:04:08 -0500 Subject: [PATCH 075/487] Attempted to the layout er I mean attempted to define the ascent, desceitn, er descent, and leading for the NSLayoutManager based code AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA. --- darwin/drawtext.m | 51 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 69166610..79aace57 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -9,6 +9,9 @@ @property NSRect lineUsedRect; @property NSRect glyphBoundingRect; @property CGFloat baselineOffset; +@property double ascent; +@property double descent; +@property double leading; @end @implementation lineInfo @@ -111,23 +114,47 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto index = 0; while (index < [tl->layoutManager numberOfGlyphs]) { NSRange glyphRange; - NSRect lineRect; - lineInfo *li; + __block lineInfo *li; - lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange]; li = [lineInfo new]; + li.lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange]; li.glyphRange = glyphRange; li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL]; - li.lineRect = lineRect; li.lineUsedRect = [tl->layoutManager lineFragmentUsedRectForGlyphAtIndex:index effectiveRange:NULL]; li.glyphBoundingRect = [tl->layoutManager boundingRectForGlyphRange:li.glyphRange inTextContainer:tl->container]; // and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index]; + li.ascent = 0; + li.descent = 0; + li.leading = 0; + // imitate what AppKit actually does (or seems to) + [tl->attrstr enumerateAttributesInRange:li.characterRange options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + NSFont *f; + double v; + + f = (NSFont *) [attrs objectForKey:NSFontAttributeName]; + if (f == nil) { + f = [NSFont fontWithName:@"Helvetica" size:12.0]; + if (f == nil) + f = [NSFont systemFontOfSize:12.0]; + } + +NSLog(@"%@ %g %g %g", NSStringFromRect([f boundingRectForFont]), +[f ascender], [f descender], [f leading]); + + v = floor([f ascender] + 0.5); + if (li.ascent < v) + li.ascent = v; + + v = floor(-[f descender] + 0.5); + if (li.descent < v) + li.descent = v; + + v = floor([f leading] + 0.5); + if (li.leading < v) + li.leading = v; + }]; [tl->lineInfo addObject:li]; -NSLog(@"line %d", (int)[tl->lineInfo count]); -NSLog(@" rect %@", NSStringFromRect(li.lineRect)); -NSLog(@" used rect %@", NSStringFromRect(li.lineUsedRect)); -NSLog(@"glyph rect %@", NSStringFromRect(li.glyphBoundingRect)); [li release]; index = glyphRange.location + glyphRange.length; } @@ -211,11 +238,9 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa // TODO is this correct? m->BaselineY = (m->Y + m->Height) - li.baselineOffset; - - // TODO - m->Ascent = 10000; - m->Descent = 10000; - m->Leading = 10000; + m->Ascent = li.ascent; + m->Descent = li.descent; + m->Leading = li.leading; // TODO m->ParagraphSpacingBefore = 0; From 877ffa5f897c3ad3065ff43ea888aa40ff37e22f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 10:36:13 -0500 Subject: [PATCH 076/487] More attempts. Ugggggh. --- darwin/drawtext.m | 56 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 79aace57..4747e58b 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -130,7 +130,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // imitate what AppKit actually does (or seems to) [tl->attrstr enumerateAttributesInRange:li.characterRange options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { NSFont *f; - double v; + NSRect boundingRect; + double v, realAscent, realDescent, realLeading; + BOOL skipAdjust, doAdjust; f = (NSFont *) [attrs objectForKey:NSFontAttributeName]; if (f == nil) { @@ -139,21 +141,63 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto f = [NSFont systemFontOfSize:12.0]; } -NSLog(@"%@ %g %g %g", NSStringFromRect([f boundingRectForFont]), -[f ascender], [f descender], [f leading]); + boundingRect = [f boundingRectForFont]; + realAscent = [f ascender]; + realDescent = -[f descender]; + realLeading = [f leading]; + skipAdjust = NO; + doAdjust = NO; + if (NSMaxY(boundingRect) <= realAscent) { + // ascent entirely within bounding box + // don't do anything if there's leading; I'm guessing it's a combination of both of the reasons to skip below... (least sure of this one) + if (realLeading != 0) + skipAdjust = YES; + // does the descent slip outside the bounding box? + if (-realDescent <= NSMinY(boundingRect)) + // yes — I guess we should assume accents don't collide with the previous line's descent, though I'm not as sure of that as I am about the else clause below... + skipAdjust = YES; + } else { + // ascent outside bounding box — ascent does not include accents + // only skip adjustment if there isn't leading (apparently some fonts use the previous line's leading for accents? :/ ) + if (realLeading != 0) + skipAdjust = YES; + } + if (!skipAdjust) { + UniChar ch = 0xC0; + CGGlyph glyph; - v = floor([f ascender] + 0.5); + // there does not seem to be an AppKit API for this... + if (CTFontGetGlyphsForCharacters((CTFontRef) f, &ch, &glyph, 1) != false) { + NSRect bbox; + + bbox = [f boundingRectForGlyph:glyph]; + if (NSMaxY(bbox) > realAscent) + doAdjust = YES; + if (-realDescent > NSMinY(bbox)) + doAdjust = YES; + } + } + // TODO vertical + + v = [f ascender]; + // TODO get this one back out + if (doAdjust) + v += 0.2 * ([f ascender] + [f descender]); + //v = floor(v + 0.5); if (li.ascent < v) li.ascent = v; - v = floor(-[f descender] + 0.5); + v = -[f descender];// floor(-[f descender] + 0.5); if (li.descent < v) li.descent = v; - v = floor([f leading] + 0.5); + v = [f leading];//floor([f leading] + 0.5); if (li.leading < v) li.leading = v; }]; + li.ascent = floor(li.ascent + 0.5); + li.descent = floor(li.descent + 0.5); + li.leading = floor(li.leading + 0.5); [tl->lineInfo addObject:li]; [li release]; index = glyphRange.location + glyphRange.length; From 8d3c68d7f020990b307d8829bd49b2f93554619e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 17:12:17 -0500 Subject: [PATCH 077/487] Moved the AppKit text drawer out of the way for now. One last experiment first; didn't seem to matter :S --- darwin/{drawtext.m => _appkit_drawtext.m} | 1 + darwin/{fontmatch.m => _appkit_fontmatch.m} | 0 darwin/uipriv_darwin.h | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) rename darwin/{drawtext.m => _appkit_drawtext.m} (99%) rename darwin/{fontmatch.m => _appkit_fontmatch.m} (100%) diff --git a/darwin/drawtext.m b/darwin/_appkit_drawtext.m similarity index 99% rename from darwin/drawtext.m rename to darwin/_appkit_drawtext.m index 4747e58b..644a2bdf 100644 --- a/darwin/drawtext.m +++ b/darwin/_appkit_drawtext.m @@ -101,6 +101,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto [tl->container setLineFragmentPadding:0]; tl->layoutManager = [[NSLayoutManager alloc] init]; + [tl->layoutManager setTypesetterBehavior:NSTypesetterLatestBehavior]; [tl->layoutManager addTextContainer:tl->container]; [tl->attrstr addLayoutManager:tl->layoutManager]; diff --git a/darwin/fontmatch.m b/darwin/_appkit_fontmatch.m similarity index 100% rename from darwin/fontmatch.m rename to darwin/_appkit_fontmatch.m diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index bfbf0688..0a15e3bf 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -147,5 +147,5 @@ extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); // fontmatch.m -//TODO extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd); From 8ff01c5034f8000393466f5e30dc1edca2a584d9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 17:15:57 -0500 Subject: [PATCH 078/487] Ugh --- darwin/_coretext_drawtext.m | 489 ++++++++++++++++++++---------------- 1 file changed, 274 insertions(+), 215 deletions(-) diff --git a/darwin/_coretext_drawtext.m b/darwin/_coretext_drawtext.m index 72a8d2e6..a84b68b5 100644 --- a/darwin/_coretext_drawtext.m +++ b/darwin/_coretext_drawtext.m @@ -1,268 +1,327 @@ -// 6 september 2015 +// 2 january 2017 #import "uipriv_darwin.h" +#import "draw.h" -// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) -struct uiDrawFontFamilies { - CFArrayRef fonts; +// TODO what happens if nLines == 0 in any function? + +struct uiDrawTextLayout { + CFAttributedStringRef attrstr; + + // the width as passed into uiDrawTextLayout constructors + double width; + + CTFramesetterRef framesetter; + + // the *actual* size of the frame + // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) + // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path + // (this I confirmed through experimentation) + // so we can just use tl->size for adjustments + // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) + CGSize size; + + CGPathRef path; + CTFrameRef frame; + + CFArrayRef lines; + CFIndex nLines; + // we compute this once when first creating the layout + uiDrawTextLayoutLineMetrics *lineMetrics; + + // for converting CFAttributedString indices to byte offsets + size_t *u16tou8; + size_t nu16tou8; // TODO I don't like the casing of this name }; -uiDrawFontFamilies *uiDrawListFontFamilies(void) +static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) { - uiDrawFontFamilies *ff; + CTFontDescriptorRef desc; + CTFontRef font; - ff = uiNew(uiDrawFontFamilies); - ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); - if (ff->fonts == NULL) - implbug("error getting available font names (no reason specified) (TODO)"); - return ff; -} - -int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) -{ - return CFArrayGetCount(ff->fonts); -} - -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) -{ - CFStringRef familystr; - char *family; - - familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); - // toll-free bridge - family = uiDarwinNSStringToText((NSString *) familystr); - // Get Rule means we do not free familystr - return family; -} - -void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) -{ - CFRelease(ff->fonts); - uiFree(ff); -} - -struct uiDrawTextFont { - CTFontRef f; -}; - -uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) -{ - uiDrawTextFont *font; - - font = uiNew(uiDrawTextFont); - font->f = f; - if (retain) - CFRetain(font->f); + desc = fontdescToCTFontDescriptor(fd); + font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); + CFRelease(desc); // TODO correct? return font; } -uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) -{ - // toll-free bridging; we do retain, though - return mkTextFont((CTFontRef) f, YES); -} - -static CFMutableDictionaryRef newAttrList(void) -{ - CFMutableDictionaryRef attr; - - attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (attr == NULL) - complain("error creating attribute dictionary in newAttrList()()"); - return attr; -} - -static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) +static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; + CFAttributedStringRef base; + CFMutableAttributedStringRef mas; - cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); - if (cfstr == NULL) - complain("error creating font family name CFStringRef in addFontFamilyAttr()"); - CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); - CFRelease(cfstr); // dictionary holds its own reference + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); + if (cfstr == NULL) { + // TODO + } + defaultAttrs = CFDictionaryCreateMutable(NULL, 1, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (defaultAttrs == NULL) { + // TODO + } + defaultCTFont = fontdescToCTFont(defaultFont); + CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); + CFRelease(defaultCTFont); + + base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); + if (base == NULL) { + // TODO + } + CFRelease(cfstr); + CFRelease(defaultAttrs); + mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); + CFRelease(base); + + CFAttributedStringBeginEditing(mas); + // TODO copy in the attributes + CFAttributedStringEndEditing(mas); + + return mas; } -static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) +// TODO this is wrong for our hit-test example's multiple combining character example +static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { - CFNumberRef n; + uiDrawTextLayoutLineMetrics *metrics; + CFArrayRef lines; + CTLineRef line; + CFIndex i, n; + CGFloat ypos; + CGRect bounds, boundsNoLeading; + CGFloat ascent, descent, leading; + CGPoint *origins; - n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); - CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); - CFRelease(n); + lines = CTFrameGetLines(frame); + n = CFArrayGetCount(lines); + metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + + origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)"); + CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); + + ypos = size.height; + for (i = 0; i < n; i++) { + line = (CTLineRef) CFArrayGetValueAtIndex(lines, i); + bounds = CTLineGetBoundsWithOptions(line, 0); + boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); + + // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) + ascent = bounds.size.height + bounds.origin.y; + descent = -boundsNoLeading.origin.y; + // TODO does this preserve leading sign? + leading = -bounds.origin.y - descent; + + // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) + ascent = floor(ascent + 0.5); + descent = floor(descent + 0.5); + if (leading > 0) + leading = floor(leading + 0.5); + + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; + metrics[i].Height = ascent + descent + leading; + + metrics[i].BaselineY = origins[i].y; + metrics[i].Ascent = ascent; + metrics[i].Descent = descent; + metrics[i].Leading = leading; + + // TODO + metrics[i].ParagraphSpacingBefore = 0; + metrics[i].LineHeightSpace = 0; + metrics[i].LineSpacing = 0; + metrics[i].ParagraphSpacing = 0; + + // and finally advance to the next line + ypos += metrics[i].Height; + } + + // okay, but now all these metrics are unflipped + // we need to flip them + for (i = 0; i < n; i++) { + metrics[i].Y = size.height - metrics[i].Y; + // go from bottom-left corner to top-left + metrics[i].Y -= metrics[i].Height; + metrics[i].BaselineY = size.height - metrics[i].BaselineY; + // TODO also adjust by metrics[i].Height? + } + + uiFree(origins); + return metrics; } -#if 0 -TODO -// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do -// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D -static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { - CFMutableArrayRef outerArray; - CFMutableDictionaryRef innerDict; - CFNumberRef numType, numSelector; - int num; + uiDrawTextLayout *tl; + CGFloat cgwidth; + CFRange range, unused; + CGRect rect; - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) - complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); + tl = uiNew(uiDrawTextLayout); + tl->attrstr = attrstrToCoreFoundation(s, defaultFont); + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + tl->width = width; - // Apple's headers say these are deprecated, but a few fonts still rely on them - num = kLetterCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray + // TODO CTFrameProgression for RTL/LTR + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); + if (tl->framesetter == NULL) { + // TODO + } - // these are the non-deprecated versions of the above; some fonts have these instead - num = kLowerCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kLowerCaseSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray + cgwidth = (CGFloat) width; + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + // TODO these seem to be floor()'d or truncated? + // TODO double check to make sure this TODO was right + tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL (TODO really?) - CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); - CFRelease(outerArray); + rect.origin = CGPointZero; + rect.size = tl->size; + tl->path = CGPathCreateWithRect(rect, NULL); + tl->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + tl->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (tl->frame == NULL) { + // TODO + } + + tl->lines = CTFrameGetLines(tl->frame); + tl->nLines = CFArrayGetCount(tl->lines); + tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); + + // and finally copy the UTF-16 to UTF-8 index conversion table + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + + return tl; } -#endif -#if 0 -// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( -// kode54 got these for me before I had access to El Capitan; thanks to him. -#define ourNSFontWeightUltraLight -0.800000 -#define ourNSFontWeightThin -0.600000 -#define ourNSFontWeightLight -0.400000 -#define ourNSFontWeightRegular 0.000000 -#define ourNSFontWeightMedium 0.230000 -#define ourNSFontWeightSemibold 0.300000 -#define ourNSFontWeightBold 0.400000 -#define ourNSFontWeightHeavy 0.560000 -#define ourNSFontWeightBlack 0.620000 -#endif - -// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. -CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - CFDictionaryRef dict; - CFMutableDictionaryRef mdict; - - dict = CTFontDescriptorCopyAttributes(desc); - // this might not be mutable, so make a mutable copy - mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); - CFRelease(dict); - return mdict; + uiFree(tl->u16tou8); + uiFree(tl->lineMetrics); + // TODO release tl->lines? + CFRelease(tl->frame); + CFRelease(tl->path); + CFRelease(tl->framesetter); + CFRelease(tl->attrstr); + uiFree(tl); } -uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +// TODO document that (x,y) is the top-left corner of the *entire frame* +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - CTFontRef f; - CFMutableDictionaryRef attr; - CTFontDescriptorRef cfdesc; + CGContextSaveGState(c->c); - attr = newAttrList(); - addFontFamilyAttr(attr, desc->Family); - addFontSizeAttr(attr, desc->Size); + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped + // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) + // TODO how is this affected by a non-identity CTM? + CGContextTranslateCTM(c->c, 0, c->height); + CGContextScaleCTM(c->c, 1.0, -1.0); + CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); - // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back - cfdesc = CTFontDescriptorCreateWithAttributes(attr); - // TODO release attr? - cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); + // wait, that's not enough; we need to offset y values to account for our new flipping + // TODO explain this calculation + y = c->height - tl->size.height - y; - // specify the initial size again just to be safe - f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); - // TODO release cfdesc? + // CTFrameDraw() draws in the path we specified when creating the frame + // this means that in our usage, CTFrameDraw() will draw at (0,0) + // so move the origin to be at (x,y) instead + // TODO are the signs correct? + CGContextTranslateCTM(c->c, x, y); - return mkTextFont(f, NO); // we hold the initial reference; no need to retain again + CTFrameDraw(tl->frame, c->c); + + CGContextRestoreGState(c->c); } -void uiDrawFreeTextFont(uiDrawTextFont *font) +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral +// TODO width doesn't include trailing whitespace... +// TODO figure out how paragraph spacing should play into this +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - CFRelease(font->f); - uiFree(font); + *width = tl->size.width; + *height = tl->size.height; } -uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - return (uintptr_t) (font->f); + return tl->nLines; } -void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - // TODO + CTLineRef lr; + CFRange range; + + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + *end = tl->u16tou8[range.location + range.length]; } -// text sizes and user space points are identical: -// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch -// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch -void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - metrics->Ascent = CTFontGetAscent(font->f); - metrics->Descent = CTFontGetDescent(font->f); - metrics->Leading = CTFontGetLeading(font->f); - metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); - metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); + *m = tl->lineMetrics[line]; } -// LONGTERM allow line separation and leading to be factored into a wrapping text layout - -// TODO reconcile differences in character wrapping on platforms -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { - struct framesetter fs; + CFIndex i; + CTLineRef line; + CFIndex pos; - mkFramesetter(layout, &fs); - *width = fs.extents.width; - *height = fs.extents.height; - freeFramesetter(&fs); + if (y >= 0) { + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + if (y >= ltop && y < lbottom) + break; + } + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (i == tl->nLines) { + i--; + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + } + } else { + i = 0; + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + } + result->Line = i; + + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < tl->lineMetrics[i].X) { + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + // and forcibly return the first character + x = tl->lineMetrics[i].X; + } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + // and forcibly return the last character + x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; + } + + line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + // TODO copy the part from the docs about this point + pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); + if (pos == kCFNotFound) { + // TODO + } + result->Pos = tl->u16tou8[pos]; } -// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? - -// LONGTERM keep this for later features and documentation purposes -#if 0 - - // LONGTERM provide a way to get the image bounds as a separate function later - bounds = CTLineGetImageBounds(line, c); - // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error - - // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead - CTLineGetTypographicBounds(line, &yoff, NULL, NULL); - // remember that we're flipped, so we subtract - y -= yoff; - CGContextSetTextPosition(c, x, y); -#endif - -#if 0 -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { - CGColorSpaceRef colorspace; - CGFloat components[4]; - CGColorRef color; - - // for consistency with windows, use sRGB - colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = a; - color = CGColorCreate(colorspace, components); - CGColorSpaceRelease(colorspace); - - CFAttributedStringSetAttribute(layout->mas, - rangeToCFRange(), - kCTForegroundColorAttributeName, - color); - CGColorRelease(color); // TODO safe? } -#endif From c9e7ee3a92324bce316a86cd4fa72817bdadd72a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 23:13:44 -0500 Subject: [PATCH 079/487] Reactivated the Core Text backend. --- darwin/{_coretext_drawtext.m => drawtext.m} | 0 darwin/{_coretext_fontmatch.m => fontmatch.m} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename darwin/{_coretext_drawtext.m => drawtext.m} (100%) rename darwin/{_coretext_fontmatch.m => fontmatch.m} (100%) diff --git a/darwin/_coretext_drawtext.m b/darwin/drawtext.m similarity index 100% rename from darwin/_coretext_drawtext.m rename to darwin/drawtext.m diff --git a/darwin/_coretext_fontmatch.m b/darwin/fontmatch.m similarity index 100% rename from darwin/_coretext_fontmatch.m rename to darwin/fontmatch.m From 7614d3e0ff53f2ddb704f4d446adaf09ad6b0836 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 Jan 2017 15:39:24 -0500 Subject: [PATCH 080/487] More TODOs. --- windows/sizing.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/sizing.cpp b/windows/sizing.cpp index a6d25d6e..33cc00b9 100644 --- a/windows/sizing.cpp +++ b/windows/sizing.cpp @@ -51,6 +51,7 @@ void uiWindowsSizingDlgUnitsToPixels(uiWindowsSizing *sizing, int *x, int *y) // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing and https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx // this X value is really only for buttons but I don't see a better one :/ #define winXPadding 4 +// TODO is this too much? #define winYPadding 4 void uiWindowsSizingStandardPadding(uiWindowsSizing *sizing, int *x, int *y) From 9063b106910a438b5149e923a3aae3c5b212f3e7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 Jan 2017 15:33:17 -0500 Subject: [PATCH 081/487] More notes. --- hidpinotes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 hidpinotes diff --git a/hidpinotes b/hidpinotes new file mode 100644 index 00000000..66acc0f3 --- /dev/null +++ b/hidpinotes @@ -0,0 +1,2 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx From ea93528cbae3de13d6b2886295a062a02e66e62c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 Jan 2017 03:21:15 -0500 Subject: [PATCH 082/487] IMPORTANT hidpi notes. --- hidpinotes | 1 + 1 file changed, 1 insertion(+) diff --git a/hidpinotes b/hidpinotes index 66acc0f3..74b1b021 100644 --- a/hidpinotes +++ b/hidpinotes @@ -1,2 +1,3 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx +!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli From bb52275686cf2a5269d8b94c20f4bcc9464d66af Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 Jan 2017 21:50:38 -0500 Subject: [PATCH 083/487] Corrected ambiguous wording in a comment. --- windows/tabpage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/tabpage.cpp b/windows/tabpage.cpp index 5283ce79..77df9a18 100644 --- a/windows/tabpage.cpp +++ b/windows/tabpage.cpp @@ -60,7 +60,7 @@ static INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPar if (uMsg == WM_WINDOWPOSCHANGED) { tp = (struct tabPage *) GetWindowLongPtrW(hwnd, DWLP_USER); tabPageRelayout(tp); - // pretend the dialog hasn't handled this just in case it needs to do something special + // pretend the dialog hasn't handled this just in case the system needs to do something special return FALSE; } From c336063b65caa390758aa68ddb3e2cafbd414605 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 20:26:59 -0500 Subject: [PATCH 084/487] Decided what I need to do. --- darwin/drawtext.m | 8 ++++++-- examples/drawtext/hittest.c | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a84b68b5..fb9e3133 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -276,6 +276,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } +// TODO note that in some cases lines can overlap slightly +// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { CFIndex i; @@ -288,7 +290,8 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex ltop = tl->lineMetrics[i].Y; lbottom = ltop + tl->lineMetrics[i].Height; - if (y >= ltop && y < lbottom) + // y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop + if (y < lbottom) break; } result->YPosition = uiDrawTextLayoutHitTestPositionInside; @@ -298,6 +301,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex } } else { i = 0; + // TODO what if the first line crosses into the negatives? result->YPosition = uiDrawTextLayoutHitTestPositionBefore; } result->Line = i; @@ -314,7 +318,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex } line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - // TODO copy the part from the docs about this point + // TODO copy the part from the docs about this point (TODO what point?) pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); if (pos == kCFNotFound) { // TODO diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index a2454549..dc1f9dce 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,8 +3,8 @@ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " -//TODO "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " -//TODO "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " + "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " + "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " "\xC3\x93O\xCC\x81 (combining accents) " From b18cc88dce2c874878dc47fbde437b2fd2ce4bf2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 20:42:52 -0500 Subject: [PATCH 085/487] Boilerplate needed for implementing range-to-rect. --- common/attrstr.c | 13 +++++++++++++ common/uipriv.h | 1 + darwin/drawtext.m | 12 ++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index bfe42842..03dab8f9 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -315,11 +315,24 @@ size_t attrstrUTF16Len(uiAttributedString *s) return s->u16len; } +// TODO is this still needed given the below? size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n) { return s->u8tou16[n]; } +size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n) +{ + size_t *out; + size_t nbytes; + + nbytes = (s->len + 1) * sizeof (size_t); + *n = s->len; + out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + memmove(out, s->u8tou16, nbytes); + return out; +} + size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n) { size_t *out; diff --git a/common/uipriv.h b/common/uipriv.h index e62e86f8..0ad18c14 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -73,6 +73,7 @@ extern struct graphemes *graphemes(void *s, size_t len); extern const uint16_t *attrstrUTF16(uiAttributedString *s); extern size_t attrstrUTF16Len(uiAttributedString *s); extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); // attrlist.c diff --git a/darwin/drawtext.m b/darwin/drawtext.m index fb9e3133..4db68fad 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -28,9 +28,11 @@ struct uiDrawTextLayout { // we compute this once when first creating the layout uiDrawTextLayoutLineMetrics *lineMetrics; - // for converting CFAttributedString indices to byte offsets + // for converting CFAttributedString indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; size_t *u16tou8; - size_t nu16tou8; // TODO I don't like the casing of this name + size_t nUTF16; }; static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) @@ -201,8 +203,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl->nLines = CFArrayGetCount(tl->lines); tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); - // and finally copy the UTF-16 to UTF-8 index conversion table - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + // and finally copy the UTF-8/UTF-16 conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); return tl; } @@ -210,6 +213,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); + uiFree(tl->u8tou16); uiFree(tl->lineMetrics); // TODO release tl->lines? CFRelease(tl->frame); From 0ae25c62ed02951abd215ffe3f648432fe88720a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 21:17:48 -0500 Subject: [PATCH 086/487] Implemented the range-to-rect function on OS X. --- darwin/drawtext.m | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 4db68fad..f3c8e518 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -330,6 +330,46 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex result->Pos = tl->u16tou8[pos]; } +// TODO document this is appropriate for a caret +// TODO what happens if we select across a wrapped line? void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { + CFIndex i; + CTLineRef line; + CFRange range; + CGFloat x, x2; // TODO rename x to x1 + + if (start > tl->nUTF8) + start = tl->nUTF8; + if (end > tl->nUTF8) + end = tl->nUTF8; + start = tl->u8tou16[start]; + end = tl->u8tou16[end]; + + for (i = 0; i < tl->nLines; i++) { + line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + range = CTLineGetStringRange(line); + if (start >= range.location) + break; + } + if (i == tl->nLines) + i--; + r->Line = i; + if (end > (range.location + range.length)) + end = range.location + range.length; + + x = CTLineGetOffsetForStringIndex(line, start, NULL); + x2 = CTLineGetOffsetForStringIndex(line, end, NULL); + + r->X = tl->lineMetrics[i].X + x; + r->Y = tl->lineMetrics[i].Y; + r->Width = (tl->lineMetrics[i].X + x2) - r->X; + r->Height = tl->lineMetrics[i].Height; + + // and use x and x2 to get the actual start and end positions + // TODO error check? + r->RealStart = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); + r->RealEnd = CTLineGetStringIndexForPosition(line, CGPointMake(x2, 0)); + r->RealStart = tl->u16tou8[r->RealStart]; + r->RealEnd = tl->u16tou8[r->RealEnd]; } From 64a1167e5f042708718fc0f655563abf2e3e0bc3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 21:44:48 -0500 Subject: [PATCH 087/487] Added the blue caret to the drawtext example. Phew! --- darwin/drawtext.m | 3 ++- examples/drawtext/hittest.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f3c8e518..218c06d4 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -349,7 +349,8 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si for (i = 0; i < tl->nLines; i++) { line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); range = CTLineGetStringRange(line); - if (start >= range.location) + // TODO explain this check + if (range.location >= start) break; } if (i == tl->nLines) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index dc1f9dce..554e4a8e 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -4,6 +4,7 @@ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " + // TODO rephrase this; I don't think this code will use those grapheme functions... "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " @@ -28,6 +29,8 @@ static uiAttributedString *attrstr; static uiBox *panel; static uiCheckbox *showLineBounds; +static size_t cursorPos; + // TODO should be const? static uiDrawBrush fillBrushes[4] = { { @@ -60,10 +63,23 @@ static uiDrawBrush fillBrushes[4] = { }, }; +// TODO this should be const +static uiDrawStrokeParams strokeParams = { + .Cap = uiDrawLineCapFlat, + .Join = uiDrawLineJoinMiter, + .Thickness = 2, + .MiterLimit = uiDrawDefaultMiterLimit, + .Dashes = NULL, + .NumDashes = 0, + .DashPhase = 0, +}; + static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; + uiDrawTextLayoutByteRangeRectangle r; + uiDrawBrush brush; // only clip the text, not the guides uiDrawSave(p->Context); @@ -83,6 +99,19 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); + uiDrawTextLayoutByteRangeToRectangle(layout, cursorPos, cursorPos, &r); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, margins + r.X, margins + r.Y); + uiDrawPathLineTo(path, margins + r.X, margins + r.Y + r.Height); + uiDrawPathEnd(path); + brush.Type = uiDrawBrushTypeSolid; + brush.R = 0.0; + brush.G = 0.0; + brush.B = 1.0; + brush.A = 1.0; + uiDrawStroke(p->Context, path, &brush, &strokeParams); + uiDrawFreePath(path); + if (uiCheckboxChecked(showLineBounds)) { uiDrawTextLayoutLineMetrics m; int i, n; @@ -132,6 +161,7 @@ struct example *mkHitTestExample(void) hitTestExample.draw = draw; attrstr = uiNewAttributedString(text); + cursorPos = uiAttributedStringLen(attrstr); return &hitTestExample; } From a1bebc82d858f73ad08d9ac06b2f5eba82ee8c08 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 00:26:22 -0500 Subject: [PATCH 088/487] And implemented caret motions in the hit test examples. --- darwin/drawtext.m | 3 +-- examples/drawtext/basic.c | 1 + examples/drawtext/drawtext.h | 3 ++- examples/drawtext/hittest.c | 23 +++++++++++++++++++++++ examples/drawtext/main.c | 3 ++- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 218c06d4..452619f5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -349,8 +349,7 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si for (i = 0; i < tl->nLines; i++) { line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); range = CTLineGetStringRange(line); - // TODO explain this check - if (range.location >= start) + if (start >= range.location && start < (range.location + range.length)) break; } if (i == tl->nLines) diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index 04ea42e1..c40cf36c 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -253,6 +253,7 @@ struct example *mkBasicExample(void) basicExample.name = "Basic Paragraph of Text"; basicExample.panel = uiControl(panel); basicExample.draw = draw; + basicExample.mouse = NULL; attrstr = uiNewAttributedString(text); diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index ec821eb8..c82383eb 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -7,7 +7,8 @@ struct example { const char *name; uiControl *panel; void (*draw)(uiAreaDrawParams *p); - // TODO mouse and key? + void (*mouse)(uiAreaMouseEvent *e); + // TODO key? }; // main.c diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 554e4a8e..1896fc2a 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -133,6 +133,28 @@ static void draw(uiAreaDrawParams *p) uiDrawFreeTextLayout(layout); } +static void mouse(uiAreaMouseEvent *e) +{ + uiDrawTextLayout *layout; + uiDrawTextLayoutHitTestResult res; + + if (e->Down != 1) + return; + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + e->AreaWidth - 2 * margins); + uiDrawTextLayoutHitTest(layout, + e->X - margins, e->Y - margins, + &res); + uiDrawFreeTextLayout(layout); + + // TODO make a label and set it + + cursorPos = res.Pos; + redraw(); +} + static struct example hitTestExample; // TODO share? @@ -159,6 +181,7 @@ struct example *mkHitTestExample(void) hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); hitTestExample.draw = draw; + hitTestExample.mouse = mouse; attrstr = uiNewAttributedString(text); cursorPos = uiAttributedStringLen(attrstr); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 8e5a93ec..552b2f6b 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -31,7 +31,8 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) { - // do nothing + if (examples[curExample]->mouse != NULL) + examples[curExample]->mouse(e); } static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) From 675e7594dae7b670a9b02f5cdb00b4c0f6f81854 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 00:26:47 -0500 Subject: [PATCH 089/487] More TODOs. --- examples/drawtext/hittest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 1896fc2a..8e85436a 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -1,6 +1,8 @@ // 20 january 2017 #include "drawtext.h" +// TODO have a ligature + static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " From 71176c2e331ce34bdcc6019bac99d81855cb3e75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 01:46:06 -0500 Subject: [PATCH 090/487] Added the descriptive label to the caret. --- examples/drawtext/hittest.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 8e85436a..efa80907 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -2,6 +2,7 @@ #include "drawtext.h" // TODO have a ligature +// TODO allow clicking on the end of a line to put the caret there static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " @@ -29,6 +30,7 @@ static uiAttributedString *attrstr; #define margins 10 static uiBox *panel; +static uiLabel *caretLabel; static uiCheckbox *showLineBounds; static size_t cursorPos; @@ -135,10 +137,17 @@ static void draw(uiAreaDrawParams *p) uiDrawFreeTextLayout(layout); } +static const char *positions[] = { + [uiDrawTextLayoutHitTestPositionBefore] = "before", + [uiDrawTextLayoutHitTestPositionInside] = "inside", + [uiDrawTextLayoutHitTestPositionAfter] = "after", +}; + static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; uiDrawTextLayoutHitTestResult res; + char labelText[128]; if (e->Down != 1) return; @@ -151,7 +160,11 @@ static void mouse(uiAreaMouseEvent *e) &res); uiDrawFreeTextLayout(layout); - // TODO make a label and set it + sprintf(labelText, "pos %zd line %d x position %s y position %s", + res.Pos, res.Line, + positions[res.XPosition], + positions[res.YPosition]); + uiLabelSetText(caretLabel, labelText); cursorPos = res.Pos; redraw(); @@ -178,6 +191,8 @@ static uiCheckbox *newCheckbox(const char *text) struct example *mkHitTestExample(void) { panel = uiNewVerticalBox(); + caretLabel = uiNewLabel("Caret information is shown here"); + uiBoxAppend(panel, uiControl(caretLabel), 0); showLineBounds = newCheckbox("Show Line Bounds (for debugging metrics)"); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; From c539362c1545adfcca6856ad00601f8487efa6ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 10:11:45 -0500 Subject: [PATCH 091/487] Implemented the Pango hit test functions. Now to test. --- unix/drawtext.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/unix/drawtext.c b/unix/drawtext.c index 6c01bc7d..7c1f13e4 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -188,8 +188,65 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { + int index; + int trailing; + int line; + double width, height; + + // disregard the return value; we do our own bounds checking + // TODO see if there's a way we can use some other function (possibly this one again) to extrapolate more precise bounds information + pango_layout_xy_to_index(tl->layout, + cairoToPango(x), cairoToPango(y), + &index, &trailing); + // figure out what line that was + pango_layout_index_to_line_x(tl->layout, index, trailing, + &line, NULL); + result->Pos = index; + result->Line = line; + + uiDrawTextLayoutExtents(tl, &width, &height); + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < 0) + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (x >= width) + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (y >= height) + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; } +// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function +// TODO is this correct for RTL? void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { + PangoRectangle startRect, endRect; + int line; + PangoLayoutLine *pll; + + pango_layout_index_to_pos(tl->layout, start, &startRect); + + // figure out what line that was + // TODO talk about leading edge here + pango_layout_index_to_line_x(tl->layout, start, 1, + &line, NULL); + pll = pango_layout_get_line_readonly(tl->layout, line); + + if (end > (pll->start_index + pll->length)) + end = pll->start_index + pll->length; + pango_layout_index_to_pos(tl->layout, end, &endRect); + + r->X = pangoToCairo(startRect.x); + r->Y = tl->lineMetrics[line].Y; + r->Width = pangoToCairo(endRect.x) - r->X; + r->Height = tl->lineMetrics[line].Height; + + // and figure out the correct start pos + pango_layout_line_x_to_index(pll, startRect.x, + &index, NULL); + r->RealStart = index; + r->RealEnd = end; + + // TODO unref pll? } From 3d8bf019726d76a53116f7a5a17cce4cab782f0d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 10:30:26 -0500 Subject: [PATCH 092/487] And implemented the hit-testing functions on GTK+. --- unix/drawtext.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 7c1f13e4..b0d44833 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,6 +4,7 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? +// - the space at the end of a line seems to capture the entire end of that line, especially in hit-testing :S it should work like it does on other platforms struct uiDrawTextLayout { PangoLayout *layout; @@ -222,7 +223,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { PangoRectangle startRect, endRect; - int line; + int line, index; PangoLayoutLine *pll; pango_layout_index_to_pos(tl->layout, start, &startRect); From e4ed1c337ba62a99e8eb3438c57ffba818bbe8f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 18:38:44 -0500 Subject: [PATCH 093/487] And implemented the functions on Windows. Yeah I think I'll need cursor functions, perhaps. --- examples/drawtext/hittest.c | 6 ++- windows/drawtext.cpp | 100 +++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 8 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index efa80907..cacde315 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -160,8 +160,10 @@ static void mouse(uiAreaMouseEvent *e) &res); uiDrawFreeTextLayout(layout); - sprintf(labelText, "pos %zd line %d x position %s y position %s", - res.Pos, res.Line, + // urgh %zd is not supported by MSVC with sprintf() + // TODO get that part in test/ about having no other option + sprintf(labelText, "pos %d line %d x position %s y position %s", + (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); uiLabelSetText(caretLabel, labelText); diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 345723ce..ab63c0e1 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -5,15 +5,18 @@ // TODO // - consider the warnings about antialiasing in the PadWrite sample // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... +// - what happens if any nLines == 0? struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; UINT32 nLines; struct lineInfo *lineInfo; - // for converting DirectWrite indices to byte offsets + // for converting DirectWrite indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; size_t *u16tou8; - size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary? + size_t nUTF16; }; // TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here @@ -43,7 +46,7 @@ static std::map dwriteStretches = { }; struct lineInfo { - size_t startPos; + size_t startPos; // in UTF-16 points size_t endPos; size_t newlineCount; double x; @@ -182,8 +185,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto computeLineInfo(tl); - // and finally copy the UTF-16 to UTF-8 index conversion table - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + // and finally copy the UTF-8/UTF-16 index conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); // TODO can/should this be moved elsewhere? uiFree(wDefaultFamily); @@ -193,6 +197,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); + uiFree(tl->u8tou16); uiFree(tl->lineInfo); tl->layout->Release(); tl->format->Release(); @@ -294,11 +299,94 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } +// TODO stretches space at the end of a line to the whole line void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { - // TODO + DWRITE_HIT_TEST_METRICS m; + size_t line; + double width, height; + BOOL trailing, inside; // crashes if I skip these :/ + HRESULT hr; + + hr = tl->layout->HitTestPoint(x, y, + &trailing, &inside, + &m); + if (hr != S_OK) + logHRESULT(L"error hit-testing IDWriteTextLayout", hr); + + // figure out what line this is + if (y < 0) + line = 0; + else + for (line = 0; line < tl->nLines; line++) + if (y >= tl->lineInfo[line].y && y < (tl->lineInfo[line].y + tl->lineInfo[line].height)) + break; + if (line == tl->nLines) // last position + line--; + + result->Pos = tl->u16tou8[m.textPosition]; + result->Line = line; + + uiDrawTextLayoutExtents(tl, &width, &height); + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < 0) + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (x >= width) + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (y >= height) + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { + DWRITE_HIT_TEST_METRICS mstart, mend; + size_t line; + FLOAT x, y; // crashes if I skip these :/ + BOOL trailing, inside; // crashes if I skip these :/ + HRESULT hr; + + start = tl->u8tou16[start]; + end = tl->u8tou16[end]; + + // TODO explain why this is a leading hit + hr = tl->layout->HitTestTextPosition(start, FALSE, + &x, &y, + &mstart); + if (hr != S_OK) + logHRESULT(L"error getting rect of start position", hr); + + // figure out what line this is + for (line = 0; line < tl->nLines; line++) + if (start >= tl->lineInfo[line].startPos && start < tl->lineInfo[line].endPos) + break; + if (line == tl->nLines) // last position + line--; + if (end > tl->lineInfo[line].endPos) + end = tl->lineInfo[line].endPos; + + hr = tl->layout->HitTestTextPosition(end, FALSE, + &x, &y, + &mend); + if (hr != S_OK) + logHRESULT(L"error getting rect of end position", hr); + + r->X = mstart.left; + r->Y = tl->lineInfo[line].y; + r->Width = mend.left - mstart.left; + r->Height = tl->lineInfo[line].height; + + hr = tl->layout->HitTestPoint(r->X, r->Y, + &trailing, &inside, + &mstart); + if (hr != S_OK) + logHRESULT(L"TODO write this", hr); + // TODO also get the end pos just so we can have an accurate r->RealEnd + r->RealStart = mstart.textPosition; + r->RealEnd = end; + + r->RealStart = tl->u16tou8[r->RealStart]; + r->RealEnd = tl->u16tou8[r->RealEnd]; } From ac9aefc43a0c84885f5704db6bac1e426900386c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 15:29:12 -0500 Subject: [PATCH 094/487] Set up a new API specifically for caret positioning when hit-testing a point. Not yet implemented, just planned out. --- ui_attrstr.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 8ca835ea..9d71f7d9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -132,6 +132,12 @@ struct uiDrawTextLayoutHitTestResult { int Line; uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; + + int CaretLine; + double CaretX; + double CaretY; + // CaretWidth is decided by uiDrawCaret(). + double CaretHeight; // TODO? // int InTrailingWhitespace; // TODO? From a5dac818555e454eff1d335c89b174d6f6e30a2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 16:21:18 -0500 Subject: [PATCH 095/487] Refined the hit-test API some more. --- ui_attrstr.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9d71f7d9..f82a0a63 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -128,12 +128,20 @@ _UI_ENUM(uiDrawTextLayoutHitTestPosition) { }; struct uiDrawTextLayoutHitTestResult { + // The byte position of the character at the given point. size_t Pos; + // Line is the line at the given point. If the point is on the space + // after the end of a line, Pos will be Line's end index. In this case, + // the rectangle for that character will actually be on the *next* + // line. This disparity is only relevant for caret positioning when + // using the mouse; in that case, to ensure proper behavior, use + // the Caret fields below. In all other cases (including keyboard + // caret movement), use the actual rectangle for the grapheme + // at Pos (via uiDrawTextLayoutByteRangeToRectangle()). int Line; uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; - int CaretLine; double CaretX; double CaretY; // CaretWidth is decided by uiDrawCaret(). From bbc03a489edd108d73c7a43cb22ebbb44260d76f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 16:43:49 -0500 Subject: [PATCH 096/487] Implemented the system on the hit-test example and on OS X. --- darwin/drawtext.m | 8 ++++++++ examples/drawtext/hittest.c | 28 +++++++++++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 452619f5..7364088e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -328,6 +328,14 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex // TODO } result->Pos = tl->u16tou8[pos]; + + // desired caret behavior: clicking on the right trailing space places inserted text at the start of the next line but the caret is on the clicked line at the trailing edge of the last grapheme + // hitting Right moves to the second point of the next line, then Left to go to the first, then Left to go to the start of the last character of the original line (so the keyboard can't move the caret back to that clicked space) + // this happens regardless of word-wrapping on whitespace, hyphens, or very long words + // use CTLineGetOffsetForStringIndex() here instead of x directly as that isn't always adjusted properly + result->CaretX = CTLineGetOffsetForStringIndex(line, pos, NULL); + result->CaretY = tl->lineMetrics[i].Y; + result->CaretHeight = tl->lineMetrics[i].Height; } // TODO document this is appropriate for a caret diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index cacde315..364dd1ca 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -33,7 +33,9 @@ static uiBox *panel; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; -static size_t cursorPos; +static int caretInit = 0; +// TODO rename all this to caret, as well as in the text above +static double cursorX, cursorY, cursorHeight; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -82,7 +84,6 @@ static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; - uiDrawTextLayoutByteRangeRectangle r; uiDrawBrush brush; // only clip the text, not the guides @@ -103,10 +104,21 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); - uiDrawTextLayoutByteRangeToRectangle(layout, cursorPos, cursorPos, &r); + if (!caretInit) { + uiDrawTextLayoutByteRangeRectangle r; + + uiDrawTextLayoutByteRangeToRectangle(layout, + uiAttributedStringLen(attrstr), + uiAttributedStringLen(attrstr), + &r); + cursorX = r.X; + cursorY = r.Y; + cursorHeight = r.Height; + caretInit = 1; + } path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(path, margins + r.X, margins + r.Y); - uiDrawPathLineTo(path, margins + r.X, margins + r.Y + r.Height); + uiDrawPathNewFigure(path, margins + cursorX, margins + cursorY); + uiDrawPathLineTo(path, margins + cursorX, margins + cursorY + cursorHeight); uiDrawPathEnd(path); brush.Type = uiDrawBrushTypeSolid; brush.R = 0.0; @@ -162,13 +174,16 @@ static void mouse(uiAreaMouseEvent *e) // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option + // TODO byte 1 is actually byte 684?! sprintf(labelText, "pos %d line %d x position %s y position %s", (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); uiLabelSetText(caretLabel, labelText); - cursorPos = res.Pos; + cursorX = res.CaretX; + cursorY = res.CaretY; + cursorHeight = res.CaretHeight; redraw(); } @@ -203,7 +218,6 @@ struct example *mkHitTestExample(void) hitTestExample.mouse = mouse; attrstr = uiNewAttributedString(text); - cursorPos = uiAttributedStringLen(attrstr); return &hitTestExample; } From 01b6a16af6bda2436090f83b06610e6e864d4ea4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 19:14:51 -0500 Subject: [PATCH 097/487] And handled caret behavior on GTK+. --- unix/drawtext.c | 21 +++++++++++++++++---- unix/multilineentry.c | 2 ++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index b0d44833..88cc8726 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,7 +4,6 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? -// - the space at the end of a line seems to capture the entire end of that line, especially in hit-testing :S it should work like it does on other platforms struct uiDrawTextLayout { PangoLayout *layout; @@ -68,6 +67,7 @@ static void computeLineMetrics(uiDrawTextLayout *tl) m->X = pangoToCairo(lineStartPos.x); // TODO fix the whole combined not being updated shenanigans in the static build (here because ugh) m->Y = pangoToCairo(baselineY - PANGO_ASCENT(lineExtents)); + // TODO this does not include the last space if any m->Width = pangoToCairo(lineExtents.width); m->Height = pangoToCairo(lineExtents.height); @@ -187,11 +187,19 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif +// caret behavior, based on GtkTextView (both 2 and 3) and Qt's text views (both 4 and 5), which are all based on TkTextView: +// - clicking on the right side of a line places the cursor at the beginning of the LAST grapheme on the line +// - pressing Right goes to the first grapheme of the next line, pressing Left goes back to the last grapheme of the original line +// - as such, there is *no* way to get the cursor on the end of the line +// - this includes whitespace, hyphens, and character wraps +// - spaces get special treatment (it seems): you don't see them there and they don't show up if you select one and only one! +// - and the best part is that word processors don't typically do this; they do what other platforms do! void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { int index; int trailing; int line; + int caretX; double width, height; // disregard the return value; we do our own bounds checking @@ -199,9 +207,10 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex pango_layout_xy_to_index(tl->layout, cairoToPango(x), cairoToPango(y), &index, &trailing); - // figure out what line that was + // figure out what line that was, and also the caret X position since we can get that here too + // TODO should we use the dedicated function instead? pango_layout_index_to_line_x(tl->layout, index, trailing, - &line, NULL); + &line, &caretX); result->Pos = index; result->Line = line; @@ -216,9 +225,13 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex result->YPosition = uiDrawTextLayoutHitTestPositionBefore; else if (y >= height) result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + + result->CaretX = pangoToCairo(caretX); + result->CaretY = tl->lineMetrics[line].Y; + result->CaretHeight = tl->lineMetrics[line].Height; } -// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function +// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function for manual positions by byte index only? // TODO is this correct for RTL? void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { diff --git a/unix/multilineentry.c b/unix/multilineentry.c index 09ffd460..228dc80f 100644 --- a/unix/multilineentry.c +++ b/unix/multilineentry.c @@ -1,6 +1,8 @@ // 6 december 2015 #include "uipriv_unix.h" +// TODO GTK_WRAP_WORD_CHAR to avoid spurious resizes? + struct uiMultilineEntry { uiUnixControl c; GtkWidget *widget; From 3e4f99e0dd87ad4de90e2b3cc562ab6e9a20fa5b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 19:42:00 -0500 Subject: [PATCH 098/487] Notes for Windows equivalent of previous commits. Not actually done yet. --- windows/drawtext.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index ab63c0e1..f87acd4b 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -107,6 +107,7 @@ static void computeLineInfo(uiDrawTextLayout *tl) // TODO verify htm.textPosition and htm.length against dtm[i]/tl->lineInfo[i]? tl->lineInfo[i].x = htm[0].left; tl->lineInfo[i].y = htm[0].top; + // TODO does this not include trailing whitespace? I forget tl->lineInfo[i].width = htm[0].width; tl->lineInfo[i].height = htm[0].height; for (j = 1; j < nFragments; j++) { @@ -299,7 +300,10 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } -// TODO stretches space at the end of a line to the whole line +// expected behavior on end of line: +// - same as OS X +// - TODO RETEST THIS ON OTHER PLATFORMS: EXCEPT: if you type something, then backspace, the cursor will move back to where you clicked! +// TODO this function does NOT yet work like that; it works like the Unix equivalent right now void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { DWRITE_HIT_TEST_METRICS m; From 012ce92b713d5fb1662db3417f0274a88754eb53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 19:55:21 -0500 Subject: [PATCH 099/487] And implemented the caret changes on Windows. --- windows/drawtext.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index f87acd4b..30114432 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -342,6 +342,10 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex result->YPosition = uiDrawTextLayoutHitTestPositionBefore; else if (y >= height) result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + + result->CaretX = m.left; // TODO is this correct? + result->CaretY = tl->lineInfo[line].y; + result->CaretHeight = tl->lineInfo[line].height; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) From 93537ebb83b64062ef531994cac62abf422c6b1b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 11:38:52 -0500 Subject: [PATCH 100/487] Simpler hit-testing APIs based on Core Text's. Microsoft's PadWrite sample shows us how to implement these on both DirectWrite and the similarly-interfaced Pango. --- ui_attrstr.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index f82a0a63..1dd04435 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -180,8 +180,12 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou _UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); -_UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); +//TODO _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); +//TODO _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); // TODO draw only a line? // TODO other layout-specific attributes (alignment, wrapping, etc.)? // TODO number of lines visible for clipping rect, range visible for clipping rect? + +_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); +// TODO line first? +_UI_EXTERN void uiDrawTextLayoutByteLocation(uiDrawTextLayout *tl, size_t pos, double *x, int *line); From 749a0cddaf4d16e17301948b9f6f6dcb1156bc86 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 19:00:14 -0500 Subject: [PATCH 101/487] Wrote the new hit-testing API on OS X, the easiest target. Also updated the example. Had to slightly modify one function for this all to work. --- darwin/drawtext.m | 47 +++++++++++++++++++++++++++++++++++++ examples/drawtext/hittest.c | 40 +++++++++++++++---------------- ui_attrstr.h | 3 +-- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 7364088e..53763ce3 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -280,6 +280,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } +#if 0 /* TODO */ + // TODO note that in some cases lines can overlap slightly // in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) @@ -381,3 +383,48 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si r->RealStart = tl->u16tou8[r->RealStart]; r->RealEnd = tl->u16tou8[r->RealEnd]; } + +#endif + +// TODO note that in some cases lines can overlap slightly +// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + int i; + CTLineRef ln; + CFIndex p; + + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + // y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop + if (y < lbottom) + break; + } + if (i == tl->nLines) + i--; + if (line != NULL) + *line = i; + + // TODO do the hit-test unconditionally? + if (pos != NULL) { + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); + if (p == kCFNotFound) { + // TODO + } + *pos = tl->u16tou8[p]; + } +} + +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + CTLineRef ln; + + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + // TODO what happens if the byte location is not in this line? a return of 0? + // TODO check return? see if this can return 0, anyway, and if so make a note I guess + return CTLineGetOffsetForStringIndex(ln, tl->u8tou16[pos], NULL); +} diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 364dd1ca..4d7098d2 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -33,9 +33,8 @@ static uiBox *panel; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; -static int caretInit = 0; -// TODO rename all this to caret, as well as in the text above -static double cursorX, cursorY, cursorHeight; +static int caretLine = -1; +static double caretX; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -84,6 +83,7 @@ static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; + uiDrawTextLayoutLineMetrics m; uiDrawBrush brush; // only clip the text, not the guides @@ -104,21 +104,16 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); - if (!caretInit) { - uiDrawTextLayoutByteRangeRectangle r; - - uiDrawTextLayoutByteRangeToRectangle(layout, + if (caretLine == -1) { + caretLine = uiDrawTextLayoutNumLines(layout) - 1; + caretX = uiDrawTextLayoutByteLocationInLine(layout, uiAttributedStringLen(attrstr), - uiAttributedStringLen(attrstr), - &r); - cursorX = r.X; - cursorY = r.Y; - cursorHeight = r.Height; - caretInit = 1; + caretLine); } + uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(path, margins + cursorX, margins + cursorY); - uiDrawPathLineTo(path, margins + cursorX, margins + cursorY + cursorHeight); + uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); + uiDrawPathLineTo(path, margins + caretX, margins + m.Y + m.Height); uiDrawPathEnd(path); brush.Type = uiDrawBrushTypeSolid; brush.R = 0.0; @@ -129,7 +124,6 @@ static void draw(uiAreaDrawParams *p) uiDrawFreePath(path); if (uiCheckboxChecked(showLineBounds)) { - uiDrawTextLayoutLineMetrics m; int i, n; int fill = 0; @@ -158,7 +152,8 @@ static const char *positions[] = { static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; - uiDrawTextLayoutHitTestResult res; +// uiDrawTextLayoutHitTestResult res; + size_t pos; char labelText[128]; if (e->Down != 1) @@ -169,22 +164,25 @@ static void mouse(uiAreaMouseEvent *e) e->AreaWidth - 2 * margins); uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, - &res); +// &res); + &pos, &caretLine); + caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); uiDrawFreeTextLayout(layout); // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option // TODO byte 1 is actually byte 684?! - sprintf(labelText, "pos %d line %d x position %s y position %s", +/* sprintf(labelText, "pos %d line %d x position %s y position %s", (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); +*/ sprintf(labelText, "TODO\n"); uiLabelSetText(caretLabel, labelText); - cursorX = res.CaretX; +/* cursorX = res.CaretX; cursorY = res.CaretY; cursorHeight = res.CaretHeight; - redraw(); +*/ redraw(); } static struct example hitTestExample; diff --git a/ui_attrstr.h b/ui_attrstr.h index 1dd04435..e96a1019 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -187,5 +187,4 @@ _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, u // TODO number of lines visible for clipping rect, range visible for clipping rect? _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); -// TODO line first? -_UI_EXTERN void uiDrawTextLayoutByteLocation(uiDrawTextLayout *tl, size_t pos, double *x, int *line); +_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); From 5458e10134e82ff8c51a009ae2ae305990316ecb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 20:10:34 -0500 Subject: [PATCH 102/487] Implemented the new hit-test functions on Windows. --- windows/drawtext.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 30114432..29117923 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -300,6 +300,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } +#if 0 /* TODO */ + // expected behavior on end of line: // - same as OS X // - TODO RETEST THIS ON OTHER PLATFORMS: EXCEPT: if you type something, then backspace, the cursor will move back to where you clicked! @@ -398,3 +400,76 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si r->RealStart = tl->u16tou8[r->RealStart]; r->RealEnd = tl->u16tou8[r->RealEnd]; } + +#endif + +// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + DWRITE_HIT_TEST_METRICS m; + BOOL trailing, inside; + size_t p; + HRESULT hr; + + hr = tl->layout->HitTestPoint(x, y, + &trailing, &inside, + &m); + if (hr != S_OK) + logHRESULT(L"error hit-testing IDWriteTextLayout", hr); + p = m.textPosition; + // on a trailing hit, align to the nearest cluster + if (trailing) { + DWRITE_HIT_TEST_METRICS m2; + FLOAT x, y; // crashes if I skip these :/ + + hr = tl->layout->HitTestTextPosition(m.textPosition, trailing, + &x, &y, &m2); + if (hr != S_OK) + logHRESULT(L"error aligning trailing hit to nearest cluster", hr); + p = m2.textPosition + m2.length; + } + // TODO should we just make the pointers required? + if (pos != NULL) + *pos = tl->u16tou8[p]; + + // TODO do the line detection unconditionally? + if (line != NULL) { + UINT32 i; + + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineInfo[i].y; + lbottom = ltop + tl->lineInfo[i].height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; + } + if (i == tl->nLines) + i--; + *line = i; + } +} + +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + BOOL trailing; + DWRITE_HIT_TEST_METRICS m; + FLOAT x, y; + HRESULT hr; + + pos = tl->u8tou16[pos]; + // this behavior seems correct + // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... + // TODO where does this fail? + trailing = FALSE; + if (pos != 0 && pos != tl->nUTF16 && pos == tl->lineInfo[line].endPos) { + pos--; + trailing = TRUE; + } + hr = tl->layout->HitTestTextPosition(pos, trailing, + &x, &y, &m); + if (hr != S_OK) + logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); + return x; +} From d53bc88f50c088c55a0b0f7eee1b17303f696686 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 21:19:49 -0500 Subject: [PATCH 103/487] And filled in the new functions on GTK+. Not quite right yet... --- examples/drawtext/hittest.c | 9 +++--- unix/drawtext.c | 62 +++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 4d7098d2..04daaa33 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,6 +3,7 @@ // TODO have a ligature // TODO allow clicking on the end of a line to put the caret there +// TODO the hiding and showing does not work properly on GTK+ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " @@ -172,12 +173,12 @@ static void mouse(uiAreaMouseEvent *e) // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option // TODO byte 1 is actually byte 684?! -/* sprintf(labelText, "pos %d line %d x position %s y position %s", - (int) (res.Pos), res.Line, + sprintf(labelText, "pos %d line %d x %g",// x position %s y position %s", + (int) pos, caretLine, caretX); +/* (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); -*/ sprintf(labelText, "TODO\n"); - uiLabelSetText(caretLabel, labelText); +*/ uiLabelSetText(caretLabel, labelText); /* cursorX = res.CaretX; cursorY = res.CaretY; diff --git a/unix/drawtext.c b/unix/drawtext.c index 88cc8726..879cace9 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -187,6 +187,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif +#if 0 /* TODO */ + // caret behavior, based on GtkTextView (both 2 and 3) and Qt's text views (both 4 and 5), which are all based on TkTextView: // - clicking on the right side of a line places the cursor at the beginning of the LAST grapheme on the line // - pressing Right goes to the first grapheme of the next line, pressing Left goes back to the last grapheme of the original line @@ -264,3 +266,63 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si // TODO unref pll? } + +#endif + +// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +// TODO this or the next one doesn't work right for the end of a line? it still behaves like TkTextView... +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + int p, trailing; + + pango_layout_xy_to_index(tl->layout, + cairoToPango(x), cairoToPango(y), + &p, &trailing); + // on a trailing hit, align to the nearest cluster + // fortunately Pango provides that info directly + if (trailing != 0) + p += trailing; + if (pos != NULL) + *pos = p; + + // TODO do the line detection unconditionally? + // TODO optimize the call to pango_layout_get_line_count() + if (line != NULL) { + int i; + + for (i = 0; i < pango_layout_get_line_count(tl->layout); i++) { + double ltop, lbottom; + + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; + } + if (i == pango_layout_get_line_count(tl->layout)) + i--; + *line = i; + } +} + +// TODO find a good API for indicating that this character isn't on the line for when the layout is resized and doing that recalculation... +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + PangoLayoutLine *pll; + gboolean trailing; + int pangox; + + pll = pango_layout_get_line_readonly(tl->layout, line); + // this behavior seems correct + // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... + // TODO where does this fail? + // TODO optimize everything to avoid calling strlen() + trailing = 0; + if (pos != 0 && pos != strlen(pango_layout_get_text(tl->layout)) && pos == (pll->start_index + pll->length)) { + pos--; + trailing = 1; + } + pango_layout_line_index_to_x(pll, pos, trailing, &pangox); + // TODO unref pll? + return pangoToCairo(pangox); +} From b96114e02d51d1e84b732e1cad9da4a41f4d19a6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 21:52:52 -0500 Subject: [PATCH 104/487] Fixed the hit-test example to manage the cursor more sanely. --- examples/drawtext/hittest.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 04daaa33..f655fb74 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -35,7 +35,8 @@ static uiLabel *caretLabel; static uiCheckbox *showLineBounds; static int caretLine = -1; -static double caretX; +static size_t caretPos; +//static double caretX; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -86,6 +87,7 @@ static void draw(uiAreaDrawParams *p) uiDrawTextLayout *layout; uiDrawTextLayoutLineMetrics m; uiDrawBrush brush; + double caretX; // only clip the text, not the guides uiDrawSave(p->Context); @@ -107,10 +109,10 @@ static void draw(uiAreaDrawParams *p) if (caretLine == -1) { caretLine = uiDrawTextLayoutNumLines(layout) - 1; - caretX = uiDrawTextLayoutByteLocationInLine(layout, - uiAttributedStringLen(attrstr), - caretLine); + caretPos = uiAttributedStringLen(attrstr); } + caretX = uiDrawTextLayoutByteLocationInLine(layout, + caretPos, caretLine); uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); @@ -154,7 +156,6 @@ static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; // uiDrawTextLayoutHitTestResult res; - size_t pos; char labelText[128]; if (e->Down != 1) @@ -166,15 +167,15 @@ static void mouse(uiAreaMouseEvent *e) uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, // &res); - &pos, &caretLine); - caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); + &caretPos, &caretLine); +// caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); uiDrawFreeTextLayout(layout); // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option // TODO byte 1 is actually byte 684?! - sprintf(labelText, "pos %d line %d x %g",// x position %s y position %s", - (int) pos, caretLine, caretX); + sprintf(labelText, "pos %d line %d",// x %g",// x position %s y position %s", + (int) caretPos, caretLine);//, caretX); /* (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); From 92d996f1486e71e19356fc304e0b73d677abe2bc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Feb 2017 19:20:35 -0500 Subject: [PATCH 105/487] Okay so I can't fix Pango as the behavior is hardcoded in (https://git.gnome.org/browse/pango/tree/pango/pango-layout.c?id=f4cbd27f4e5bf8490ea411190d41813e14f12165#n4204); just write some documentation and get rid of the old APIs entirely. --- ui_attrstr.h | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index e96a1019..c2ded0d0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -121,12 +121,6 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -_UI_ENUM(uiDrawTextLayoutHitTestPosition) { - uiDrawTextLayoutHitTestPositionBefore, - uiDrawTextLayoutHitTestPositionInside, - uiDrawTextLayoutHitTestPositionAfter, -}; - struct uiDrawTextLayoutHitTestResult { // The byte position of the character at the given point. size_t Pos; @@ -142,10 +136,6 @@ struct uiDrawTextLayoutHitTestResult { uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; - double CaretX; - double CaretY; - // CaretWidth is decided by uiDrawCaret(). - double CaretHeight; // TODO? // int InTrailingWhitespace; // TODO? @@ -155,16 +145,6 @@ struct uiDrawTextLayoutHitTestResult { // or just have offsets instead? in addition? }; -struct uiDrawTextLayoutByteRangeRectangle { - int Line; - double X; - double Y; - double Width; - double Height; - size_t RealStart; - size_t RealEnd; -}; - // TODO // - allow creating a layout out of a substring // - allow marking compositon strings @@ -173,6 +153,7 @@ struct uiDrawTextLayoutByteRangeRectangle { // - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) // - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) // - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) _UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); @@ -180,11 +161,28 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou _UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -//TODO _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); -//TODO _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); -// TODO draw only a line? -// TODO other layout-specific attributes (alignment, wrapping, etc.)? // TODO number of lines visible for clipping rect, range visible for clipping rect? +// TODO rewrite all this documentation + +// uiDrawTextLayoutHitTest() returns the byte offset and line closest +// to the given point. The point is relative to the top-left of the layout. +// If the point is outside the layout itself, the closest point is chosen; +// this allows the function to be used for cursor positioning with the +// mouse. Do keep the returned line in mind if used in this way; the +// user might click on the end of a line, at which point the cursor +// might be at the trailing edge of the last grapheme on the line +// (subject to the operating system's APIs). _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); + +// uiDrawTextLayoutByteLocationInLine() returns the point offset +// into the given line that the given byte position stands. This is +// relative to the line's X position (as returned by +// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to +// the top-left of the layout. This function can be used for cursor +// positioning: if start and end are the start and end of the line +// (as returned by uiDrawTextLayoutLineByteRange()), you will get +// the correct offset, even if pos is at the end of the line. If pos is not +// in the range [start, end], a negative value will be returned, +// indicating you need to move the cursor to another line. _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); From 49d36b340c4ece8e9cf5dd1b073bbf98a4e0eb2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 10:54:37 -0500 Subject: [PATCH 106/487] Started adjusting all the implementations to the new API's formal definition. There's bugs in uiAttributedString... --- darwin/drawtext.m | 20 +++++++++++++++----- examples/drawtext/hittest.c | 7 +------ ui_attrstr.h | 27 +-------------------------- 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 53763ce3..82b99ac3 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -421,10 +421,20 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) { - CTLineRef ln; + CTLineRef lr; + CFRange range; - ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); - // TODO what happens if the byte location is not in this line? a return of 0? - // TODO check return? see if this can return 0, anyway, and if so make a note I guess - return CTLineGetOffsetForStringIndex(ln, tl->u8tou16[pos], NULL); +printf("= %zd %zd ", pos, tl->nUTF8); + pos = tl->u8tou16[pos]; +printf("-> %zd %zd\n", pos, tl->nUTF16); + if (line < 0 || line >= tl->nLines) + return -1; + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + range = CTLineGetStringRange(lr); + // note: >, not >=, because the position at end is valid! +printf("%zd %zd\n", pos, (size_t)(range.location+range.length)); + if (pos < range.location || pos > (range.location + range.length)) + return -1; + // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ + return CTLineGetOffsetForStringIndex(lr, pos, NULL); } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index f655fb74..0ae2df5d 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -113,6 +113,7 @@ static void draw(uiAreaDrawParams *p) } caretX = uiDrawTextLayoutByteLocationInLine(layout, caretPos, caretLine); +printf("%g\n", caretX); uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); @@ -146,12 +147,6 @@ static void draw(uiAreaDrawParams *p) uiDrawFreeTextLayout(layout); } -static const char *positions[] = { - [uiDrawTextLayoutHitTestPositionBefore] = "before", - [uiDrawTextLayoutHitTestPositionInside] = "inside", - [uiDrawTextLayoutHitTestPositionAfter] = "after", -}; - static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; diff --git a/ui_attrstr.h b/ui_attrstr.h index c2ded0d0..a6e7f896 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -87,8 +87,6 @@ struct uiDrawFontDescriptor { typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; -typedef struct uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTestResult; -typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { // This describes the overall bounding box of the line. @@ -121,30 +119,6 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -struct uiDrawTextLayoutHitTestResult { - // The byte position of the character at the given point. - size_t Pos; - // Line is the line at the given point. If the point is on the space - // after the end of a line, Pos will be Line's end index. In this case, - // the rectangle for that character will actually be on the *next* - // line. This disparity is only relevant for caret positioning when - // using the mouse; in that case, to ensure proper behavior, use - // the Caret fields below. In all other cases (including keyboard - // caret movement), use the actual rectangle for the grapheme - // at Pos (via uiDrawTextLayoutByteRangeToRectangle()). - int Line; - uiDrawTextLayoutHitTestPosition XPosition; - uiDrawTextLayoutHitTestPosition YPosition; - -// TODO? -// int InTrailingWhitespace; -// TODO? -// double XFraction; -// extra TODO? -// double YFraction; -// or just have offsets instead? in addition? -}; - // TODO // - allow creating a layout out of a substring // - allow marking compositon strings @@ -185,4 +159,5 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // the correct offset, even if pos is at the end of the line. If pos is not // in the range [start, end], a negative value will be returned, // indicating you need to move the cursor to another line. +// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); From 6fa009a7dd31415faeaa7151d72e64d3451e2e9c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:06:37 -0500 Subject: [PATCH 107/487] Fixed uiAttributedString not putting the right position for the last character. Simple case of using the wrong variables. --- common/attrstr.c | 4 ++-- darwin/drawtext.m | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 03dab8f9..249c086f 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -207,9 +207,9 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // and adjust the prior values in the conversion tables // use <= so the terminating 0 gets updated too for (i = 0; i <= oldlen - at; i++) - s->u8tou16[at + n8 + i] += n16; + s->u8tou16[at + oldlen + i] += old16len; for (i = 0; i <= old16len - at16; i++) - s->u16tou8[at16 + n16 + i] += n8; + s->u16tou8[at16 + old16len + i] += oldlen; // and finally do the attributes attrlistInsertCharactersUnattributed(s->attrs, at, n8); diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 82b99ac3..510164bd 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -424,15 +424,12 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int CTLineRef lr; CFRange range; -printf("= %zd %zd ", pos, tl->nUTF8); pos = tl->u8tou16[pos]; -printf("-> %zd %zd\n", pos, tl->nUTF16); if (line < 0 || line >= tl->nLines) return -1; lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); range = CTLineGetStringRange(lr); // note: >, not >=, because the position at end is valid! -printf("%zd %zd\n", pos, (size_t)(range.location+range.length)); if (pos < range.location || pos > (range.location + range.length)) return -1; // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ From 8ac9e386b569d86632beb1bf97d349368957c161 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:07:44 -0500 Subject: [PATCH 108/487] More TODOs. --- common/attrstr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/attrstr.c b/common/attrstr.c index 249c086f..f62c42f2 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -121,7 +121,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s uint32_t rune; char buf[4]; uint16_t buf16[2]; - size_t n8, n16; + size_t n8, n16; // TODO make loop-local? to avoid using them in the wrong place again size_t old, old16; size_t oldlen, old16len; size_t at16; From 4b46dab775c872745a40789f1459d39d8f2e7a82 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:09:36 -0500 Subject: [PATCH 109/487] And that last fix also fixed the issue about byte 1 being wrong. --- examples/drawtext/hittest.c | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 0ae2df5d..ab0bc5f5 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -168,7 +168,6 @@ static void mouse(uiAreaMouseEvent *e) // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option - // TODO byte 1 is actually byte 684?! sprintf(labelText, "pos %d line %d",// x %g",// x position %s y position %s", (int) caretPos, caretLine);//, caretX); /* (int) (res.Pos), res.Line, From bb50440e559335b22667e6c8f26ff6239caabcc3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:14:24 -0500 Subject: [PATCH 110/487] Started cleaning up the old API. --- darwin/drawtext.m | 124 +++------------------------------------------- 1 file changed, 8 insertions(+), 116 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 510164bd..c0da5c2f 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -280,112 +280,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } -#if 0 /* TODO */ - -// TODO note that in some cases lines can overlap slightly -// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) -{ - CFIndex i; - CTLineRef line; - CFIndex pos; - - if (y >= 0) { - for (i = 0; i < tl->nLines; i++) { - double ltop, lbottom; - - ltop = tl->lineMetrics[i].Y; - lbottom = ltop + tl->lineMetrics[i].Height; - // y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop - if (y < lbottom) - break; - } - result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (i == tl->nLines) { - i--; - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - } - } else { - i = 0; - // TODO what if the first line crosses into the negatives? - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - } - result->Line = i; - - result->XPosition = uiDrawTextLayoutHitTestPositionInside; - if (x < tl->lineMetrics[i].X) { - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - // and forcibly return the first character - x = tl->lineMetrics[i].X; - } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - // and forcibly return the last character - x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; - } - - line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - // TODO copy the part from the docs about this point (TODO what point?) - pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); - if (pos == kCFNotFound) { - // TODO - } - result->Pos = tl->u16tou8[pos]; - - // desired caret behavior: clicking on the right trailing space places inserted text at the start of the next line but the caret is on the clicked line at the trailing edge of the last grapheme - // hitting Right moves to the second point of the next line, then Left to go to the first, then Left to go to the start of the last character of the original line (so the keyboard can't move the caret back to that clicked space) - // this happens regardless of word-wrapping on whitespace, hyphens, or very long words - // use CTLineGetOffsetForStringIndex() here instead of x directly as that isn't always adjusted properly - result->CaretX = CTLineGetOffsetForStringIndex(line, pos, NULL); - result->CaretY = tl->lineMetrics[i].Y; - result->CaretHeight = tl->lineMetrics[i].Height; -} - -// TODO document this is appropriate for a caret -// TODO what happens if we select across a wrapped line? -void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) -{ - CFIndex i; - CTLineRef line; - CFRange range; - CGFloat x, x2; // TODO rename x to x1 - - if (start > tl->nUTF8) - start = tl->nUTF8; - if (end > tl->nUTF8) - end = tl->nUTF8; - start = tl->u8tou16[start]; - end = tl->u8tou16[end]; - - for (i = 0; i < tl->nLines; i++) { - line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - range = CTLineGetStringRange(line); - if (start >= range.location && start < (range.location + range.length)) - break; - } - if (i == tl->nLines) - i--; - r->Line = i; - if (end > (range.location + range.length)) - end = range.location + range.length; - - x = CTLineGetOffsetForStringIndex(line, start, NULL); - x2 = CTLineGetOffsetForStringIndex(line, end, NULL); - - r->X = tl->lineMetrics[i].X + x; - r->Y = tl->lineMetrics[i].Y; - r->Width = (tl->lineMetrics[i].X + x2) - r->X; - r->Height = tl->lineMetrics[i].Height; - - // and use x and x2 to get the actual start and end positions - // TODO error check? - r->RealStart = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); - r->RealEnd = CTLineGetStringIndexForPosition(line, CGPointMake(x2, 0)); - r->RealStart = tl->u16tou8[r->RealStart]; - r->RealEnd = tl->u16tou8[r->RealEnd]; -} - -#endif - // TODO note that in some cases lines can overlap slightly // in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) @@ -405,18 +299,16 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p } if (i == tl->nLines) i--; - if (line != NULL) - *line = i; + *line = i; - // TODO do the hit-test unconditionally? - if (pos != NULL) { - ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); - if (p == kCFNotFound) { - // TODO - } - *pos = tl->u16tou8[p]; + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) + // TODO is x relative to the line origin? + p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); + if (p == kCFNotFound) { + // TODO } + *pos = tl->u16tou8[p]; } double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) From 5823c3a53e0098db7415b2f2c669d5dee7ca4470 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:52:26 -0500 Subject: [PATCH 111/487] More cleanup. --- windows/drawtext.cpp | 140 ++++++------------------------------------- 1 file changed, 19 insertions(+), 121 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 29117923..1b525657 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -300,115 +300,15 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } -#if 0 /* TODO */ - -// expected behavior on end of line: -// - same as OS X -// - TODO RETEST THIS ON OTHER PLATFORMS: EXCEPT: if you type something, then backspace, the cursor will move back to where you clicked! -// TODO this function does NOT yet work like that; it works like the Unix equivalent right now -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) -{ - DWRITE_HIT_TEST_METRICS m; - size_t line; - double width, height; - BOOL trailing, inside; // crashes if I skip these :/ - HRESULT hr; - - hr = tl->layout->HitTestPoint(x, y, - &trailing, &inside, - &m); - if (hr != S_OK) - logHRESULT(L"error hit-testing IDWriteTextLayout", hr); - - // figure out what line this is - if (y < 0) - line = 0; - else - for (line = 0; line < tl->nLines; line++) - if (y >= tl->lineInfo[line].y && y < (tl->lineInfo[line].y + tl->lineInfo[line].height)) - break; - if (line == tl->nLines) // last position - line--; - - result->Pos = tl->u16tou8[m.textPosition]; - result->Line = line; - - uiDrawTextLayoutExtents(tl, &width, &height); - result->XPosition = uiDrawTextLayoutHitTestPositionInside; - if (x < 0) - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (x >= width) - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (y < 0) - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (y >= height) - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - - result->CaretX = m.left; // TODO is this correct? - result->CaretY = tl->lineInfo[line].y; - result->CaretHeight = tl->lineInfo[line].height; -} - -void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) -{ - DWRITE_HIT_TEST_METRICS mstart, mend; - size_t line; - FLOAT x, y; // crashes if I skip these :/ - BOOL trailing, inside; // crashes if I skip these :/ - HRESULT hr; - - start = tl->u8tou16[start]; - end = tl->u8tou16[end]; - - // TODO explain why this is a leading hit - hr = tl->layout->HitTestTextPosition(start, FALSE, - &x, &y, - &mstart); - if (hr != S_OK) - logHRESULT(L"error getting rect of start position", hr); - - // figure out what line this is - for (line = 0; line < tl->nLines; line++) - if (start >= tl->lineInfo[line].startPos && start < tl->lineInfo[line].endPos) - break; - if (line == tl->nLines) // last position - line--; - if (end > tl->lineInfo[line].endPos) - end = tl->lineInfo[line].endPos; - - hr = tl->layout->HitTestTextPosition(end, FALSE, - &x, &y, - &mend); - if (hr != S_OK) - logHRESULT(L"error getting rect of end position", hr); - - r->X = mstart.left; - r->Y = tl->lineInfo[line].y; - r->Width = mend.left - mstart.left; - r->Height = tl->lineInfo[line].height; - - hr = tl->layout->HitTestPoint(r->X, r->Y, - &trailing, &inside, - &mstart); - if (hr != S_OK) - logHRESULT(L"TODO write this", hr); - // TODO also get the end pos just so we can have an accurate r->RealEnd - r->RealStart = mstart.textPosition; - r->RealEnd = end; - - r->RealStart = tl->u16tou8[r->RealStart]; - r->RealEnd = tl->u16tou8[r->RealEnd]; -} - -#endif - // this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +// TODO go back through all of these and make sure we convert coordinates properly +// TODO same for OS X void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) { DWRITE_HIT_TEST_METRICS m; BOOL trailing, inside; size_t p; + UINT32 i; HRESULT hr; hr = tl->layout->HitTestPoint(x, y, @@ -428,27 +328,20 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p logHRESULT(L"error aligning trailing hit to nearest cluster", hr); p = m2.textPosition + m2.length; } - // TODO should we just make the pointers required? - if (pos != NULL) - *pos = tl->u16tou8[p]; + *pos = tl->u16tou8[p]; - // TODO do the line detection unconditionally? - if (line != NULL) { - UINT32 i; + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; - for (i = 0; i < tl->nLines; i++) { - double ltop, lbottom; - - ltop = tl->lineInfo[i].y; - lbottom = ltop + tl->lineInfo[i].height; - // y will already >= ltop at this point since the past lbottom should == ltop - if (y < lbottom) - break; - } - if (i == tl->nLines) - i--; - *line = i; + ltop = tl->lineInfo[i].y; + lbottom = ltop + tl->lineInfo[i].height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; } + if (i == tl->nLines) + i--; + *line = i; } double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) @@ -458,7 +351,12 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int FLOAT x, y; HRESULT hr; + if (line < 0 || line >= tl->nLines) + return -1; pos = tl->u8tou16[pos]; + // note: >, not >=, because the position at endPos is valid! + if (pos < tl->lineInfo[line].startPos || pos > tl->lineInfo[line].endPos) + return -1; // this behavior seems correct // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... // TODO where does this fail? From 0e5be3229949035713c5f973d73b4767dad9719f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 15:49:36 -0500 Subject: [PATCH 112/487] And cleaned up the GTK+ code. --- unix/drawtext.c | 126 +++++++++--------------------------------------- 1 file changed, 22 insertions(+), 104 deletions(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 879cace9..49165ae3 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -8,6 +8,7 @@ struct uiDrawTextLayout { PangoLayout *layout; uiDrawTextLayoutLineMetrics *lineMetrics; + int nLines; }; // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description @@ -48,7 +49,7 @@ static void computeLineMetrics(uiDrawTextLayout *tl) int i, n; uiDrawTextLayoutLineMetrics *m; - n = pango_layout_get_line_count(tl->layout); + n = tl->nLines; // TODO remove this variable tl->lineMetrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); iter = pango_layout_get_iter(tl->layout); @@ -130,6 +131,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO attributes + tl->nLines = pango_layout_get_line_count(tl->layout); computeLineMetrics(tl); return tl; @@ -187,93 +189,12 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif -#if 0 /* TODO */ - -// caret behavior, based on GtkTextView (both 2 and 3) and Qt's text views (both 4 and 5), which are all based on TkTextView: -// - clicking on the right side of a line places the cursor at the beginning of the LAST grapheme on the line -// - pressing Right goes to the first grapheme of the next line, pressing Left goes back to the last grapheme of the original line -// - as such, there is *no* way to get the cursor on the end of the line -// - this includes whitespace, hyphens, and character wraps -// - spaces get special treatment (it seems): you don't see them there and they don't show up if you select one and only one! -// - and the best part is that word processors don't typically do this; they do what other platforms do! -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) -{ - int index; - int trailing; - int line; - int caretX; - double width, height; - - // disregard the return value; we do our own bounds checking - // TODO see if there's a way we can use some other function (possibly this one again) to extrapolate more precise bounds information - pango_layout_xy_to_index(tl->layout, - cairoToPango(x), cairoToPango(y), - &index, &trailing); - // figure out what line that was, and also the caret X position since we can get that here too - // TODO should we use the dedicated function instead? - pango_layout_index_to_line_x(tl->layout, index, trailing, - &line, &caretX); - result->Pos = index; - result->Line = line; - - uiDrawTextLayoutExtents(tl, &width, &height); - result->XPosition = uiDrawTextLayoutHitTestPositionInside; - if (x < 0) - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (x >= width) - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (y < 0) - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (y >= height) - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - - result->CaretX = pangoToCairo(caretX); - result->CaretY = tl->lineMetrics[line].Y; - result->CaretHeight = tl->lineMetrics[line].Height; -} - -// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function for manual positions by byte index only? -// TODO is this correct for RTL? -void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) -{ - PangoRectangle startRect, endRect; - int line, index; - PangoLayoutLine *pll; - - pango_layout_index_to_pos(tl->layout, start, &startRect); - - // figure out what line that was - // TODO talk about leading edge here - pango_layout_index_to_line_x(tl->layout, start, 1, - &line, NULL); - pll = pango_layout_get_line_readonly(tl->layout, line); - - if (end > (pll->start_index + pll->length)) - end = pll->start_index + pll->length; - pango_layout_index_to_pos(tl->layout, end, &endRect); - - r->X = pangoToCairo(startRect.x); - r->Y = tl->lineMetrics[line].Y; - r->Width = pangoToCairo(endRect.x) - r->X; - r->Height = tl->lineMetrics[line].Height; - - // and figure out the correct start pos - pango_layout_line_x_to_index(pll, startRect.x, - &index, NULL); - r->RealStart = index; - r->RealEnd = end; - - // TODO unref pll? -} - -#endif - -// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() -// TODO this or the next one doesn't work right for the end of a line? it still behaves like TkTextView... +// note: Pango will not let us place the cursor at the end of a line the same way other OSs do; see https://git.gnome.org/browse/pango/tree/pango/pango-layout.c?id=f4cbd27f4e5bf8490ea411190d41813e14f12165#n4204 +// ideally there'd be a way to say "I don't need this hack; I'm well behaved" but GTK+ 2 and 3 AND Qt 4 and 5 all behave like this, with the behavior seeming to date back to TkTextView, so... void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) { int p, trailing; + int i; pango_layout_xy_to_index(tl->layout, cairoToPango(x), cairoToPango(y), @@ -282,37 +203,34 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p // fortunately Pango provides that info directly if (trailing != 0) p += trailing; - if (pos != NULL) - *pos = p; + *pos = p; - // TODO do the line detection unconditionally? - // TODO optimize the call to pango_layout_get_line_count() - if (line != NULL) { - int i; + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; - for (i = 0; i < pango_layout_get_line_count(tl->layout); i++) { - double ltop, lbottom; - - ltop = tl->lineMetrics[i].Y; - lbottom = ltop + tl->lineMetrics[i].Height; - // y will already >= ltop at this point since the past lbottom should == ltop - if (y < lbottom) - break; - } - if (i == pango_layout_get_line_count(tl->layout)) - i--; - *line = i; + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; } + if (i == pango_layout_get_line_count(tl->layout)) + i--; + *line = i; } -// TODO find a good API for indicating that this character isn't on the line for when the layout is resized and doing that recalculation... double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) { PangoLayoutLine *pll; gboolean trailing; int pangox; + if (line < 0 || line >= tl->nLines) + return -1; pll = pango_layout_get_line_readonly(tl->layout, line); + // note: >, not >=, because the position at end is valid! + if (pos < pll->start_index || pos > (pll->start_index + pll->length)) + return -1; // this behavior seems correct // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... // TODO where does this fail? From b52600d9cd8ff9e7828488417d5013efb05776f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 16:16:43 -0500 Subject: [PATCH 113/487] Preparation for adding uiDrawCaret(). --- unix/area.c | 3 ++- unix/draw.c | 4 +++- unix/draw.h | 1 + unix/uipriv_unix.h | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unix/area.c b/unix/area.c index c46447cc..ca99e602 100644 --- a/unix/area.c +++ b/unix/area.c @@ -119,7 +119,8 @@ static gboolean areaWidget_draw(GtkWidget *w, cairo_t *cr) uiAreaDrawParams dp; double clipX0, clipY0, clipX1, clipY1; - dp.Context = newContext(cr); + dp.Context = newContext(cr, + gtk_widget_get_style_context(a->widget)); loadAreaSize(a, &(dp.AreaWidth), &(dp.AreaHeight)); diff --git a/unix/draw.c b/unix/draw.c index 2d7a6367..b4d18049 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -2,17 +2,19 @@ #include "uipriv_unix.h" #include "draw.h" -uiDrawContext *newContext(cairo_t *cr) +uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) { uiDrawContext *c; c = uiNew(uiDrawContext); c->cr = cr; + c->style = style; return c; } void freeContext(uiDrawContext *c) { + // free neither cr nor style; we own neither uiFree(c); } diff --git a/unix/draw.h b/unix/draw.h index dbd82ff5..9eec2846 100644 --- a/unix/draw.h +++ b/unix/draw.h @@ -3,6 +3,7 @@ // draw.c struct uiDrawContext { cairo_t *cr; + GtkStyleContext *style; }; // drawpath.c diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 0a477158..3764f8ef 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -42,7 +42,7 @@ extern GtkWidget *childBox(struct child *c); extern void childSetMargined(struct child *c, int margined); // draw.c -extern uiDrawContext *newContext(cairo_t *); +extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); extern void freeContext(uiDrawContext *); // drawtext.c From 642363ccaea3c93b49e656ce28ca5143a3647cad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 16:29:36 -0500 Subject: [PATCH 114/487] Added the uiDrawCaret() function for drawing text carets. Now to write the backend-specific code and test it out. --- common/CMakeLists.txt | 1 + common/drawtext.c | 53 +++++++++++++++++++++++++++++++++++++++++++ common/uipriv.h | 10 ++++++++ ui_attrstr.h | 2 ++ 4 files changed, 66 insertions(+) create mode 100644 common/drawtext.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index fdace350..8ecff8b3 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -6,6 +6,7 @@ list(APPEND _LIBUI_SOURCES common/areaevents.c common/control.c common/debug.c + common/drawtext.c common/matrix.c common/shouldquit.c common/userbugs.c diff --git a/common/drawtext.c b/common/drawtext.c new file mode 100644 index 00000000..41aadb2e --- /dev/null +++ b/common/drawtext.c @@ -0,0 +1,53 @@ +// 10 february 2017 +#include "../ui.h" +#include "uipriv.h" + +void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line) +{ + double xoff; + uiDrawTextLayoutLineMetrics m; + struct caretDrawParams cdp; + uiDrawPath *path; + uiDrawBrush brush; + + caretDrawParams(c, &cdp); + + xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); + if (xoff < 0) { + size_t start, end; + int incr; + + if (*line > (uiDrawTextLayoutNumLines(layout) - 1)) { + *line = (uiDrawTextLayoutNumLines(layout) - 1); + incr = -1; + } else { + uiDrawTextLayoutLineByteRange(layout, *line, &start, &end); + incr = 1; + if (end < pos) + incr = -1; + } + while (xoff < 0) { + *line += incr; + xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); + } + } + uiDrawTextLayoutLineGetMetrics(layout, *line, &m); + + uiDrawSave(c); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, + // TODO add m.X? + x + xoff, y + m.Y, + cdp.width, m.Height); + uiDrawPathEnd(path); + brush.Type = uiDrawBrushTypeSolid; + brush.R = cdp.r; + brush.G = cdp.g; + brush.B = cdp.b; + brush.A = cdp.a; + uiDrawFill(c, path, &brush); + uiDrawFreePath(path); + + uiDrawRestore(c); +} diff --git a/common/uipriv.h b/common/uipriv.h index 0ad18c14..64954c38 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -89,6 +89,16 @@ extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAtt extern struct attrlist *attrlistNew(void); extern void attrlistFree(struct attrlist *alist); +// drawtext.c +struct caretDrawParams { + double r; + double g; + double b; + double a; + double width; +}; +extern void caretDrawParams(uiDrawContext *c, struct caretDrawParams *p); + #ifdef __cplusplus } #endif diff --git a/ui_attrstr.h b/ui_attrstr.h index a6e7f896..6254fb02 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -161,3 +161,5 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // indicating you need to move the cursor to another line. // TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); + +_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); From b5b0fae052ca8cf5e1c1a72b3dec7d2f10e85ed6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 16:53:08 -0500 Subject: [PATCH 115/487] And implemented uiDrawCaret() on GTK+. --- common/drawtext.c | 6 +++--- common/uipriv.h | 3 ++- examples/drawtext/hittest.c | 6 +++++- ui_attrstr.h | 1 + unix/drawtext.c | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 41aadb2e..4c9dd200 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -10,8 +10,6 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, uiDrawPath *path; uiDrawBrush brush; - caretDrawParams(c, &cdp); - xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); if (xoff < 0) { size_t start, end; @@ -33,12 +31,14 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, } uiDrawTextLayoutLineGetMetrics(layout, *line, &m); + caretDrawParams(c, m.Height, &cdp); + uiDrawSave(c); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, // TODO add m.X? - x + xoff, y + m.Y, + x + xoff - cdp.xoff, y + m.Y, cdp.width, m.Height); uiDrawPathEnd(path); brush.Type = uiDrawBrushTypeSolid; diff --git a/common/uipriv.h b/common/uipriv.h index 64954c38..73c7eeb0 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -95,9 +95,10 @@ struct caretDrawParams { double g; double b; double a; + double xoff; double width; }; -extern void caretDrawParams(uiDrawContext *c, struct caretDrawParams *p); +extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); #ifdef __cplusplus } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index ab0bc5f5..1d836efa 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -111,9 +111,9 @@ static void draw(uiAreaDrawParams *p) caretLine = uiDrawTextLayoutNumLines(layout) - 1; caretPos = uiAttributedStringLen(attrstr); } +#if 0 caretX = uiDrawTextLayoutByteLocationInLine(layout, caretPos, caretLine); -printf("%g\n", caretX); uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); @@ -126,6 +126,10 @@ printf("%g\n", caretX); brush.A = 1.0; uiDrawStroke(p->Context, path, &brush, &strokeParams); uiDrawFreePath(path); +#else + uiDrawCaret(p->Context, margins, margins, + layout, caretPos, &caretLine); +#endif if (uiCheckboxChecked(showLineBounds)) { int i, n; diff --git a/ui_attrstr.h b/ui_attrstr.h index 6254fb02..052f067d 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -163,3 +163,4 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); +// TODO allow blinking diff --git a/unix/drawtext.c b/unix/drawtext.c index 49165ae3..175403ad 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -244,3 +244,39 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int // TODO unref pll? return pangoToCairo(pangox); } + +// note: we can't use gtk_render_insertion_cursor() because that doesn't take information about what line to render on +// we'll just recreate what it does +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + GdkColor *color; + GdkRGBA rgba; + gfloat aspectRatio; + gint width, xoff; + + gtk_style_context_get_style(c->style, + "cursor-color", &color, + "cursor-aspect-ratio", &aspectRatio, + NULL); + if (color != NULL) { + p->r = ((double) (color->red)) / 65535.0; + p->g = ((double) (color->green)) / 65535.0; + p->b = ((double) (color->blue)) / 65535.0; + p->a = 1.0; + gdk_color_free(color); + } else { + gtk_style_context_get_color(c->style, GTK_STATE_FLAG_NORMAL, &rgba); + p->r = rgba.red; + p->g = rgba.green; + p->b = rgba.blue; + p->a = rgba.alpha; + } + + // GTK+ itself uses integer arithmetic here; let's do the same + width = height * aspectRatio + 1; + // TODO this is for LTR + xoff = width / 2; + + p->xoff = xoff; + p->width = width; +} From 746e4091cb6a14b0d5117d9811e3944cef3b0329 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 17:38:17 -0500 Subject: [PATCH 116/487] Implemented uiDrawCaret() on Windows. --- windows/drawtext.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 1b525657..b7a5ccdf 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -371,3 +371,33 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); return x; } + +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + DWORD caretWidth; + + // there seems to be no defined caret color + // the best I can come up with is "inverts colors underneath" (according to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648397(v=vs.85).aspx) which I have no idea how to do (TODO) + // just return black for now + p->r = 0.0; + p->g = 0.0; + p->b = 0.0; + p->a = 1.0; + + if (SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caretWidth, 0) == 0) + // don't log the failure, fall back gracefully + // the instruction to use this comes from https://msdn.microsoft.com/en-us/library/windows/desktop/ms648399(v=vs.85).aspx + // and we have to assume GetSystemMetrics() always succeeds, so + caretWidth = GetSystemMetrics(SM_CXBORDER); + // TODO make this a function and split it out of areautil.cpp + { + FLOAT dpix, dpiy; + + // TODO can we pass NULL for dpiy? + c->rt->GetDpi(&dpix, &dpiy); + // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd756649%28v=vs.85%29.aspx (and others; search "direct2d mouse") + p->width = ((double) (caretWidth * 96)) / dpix; + } + // and there doesn't seem to be this either... (TODO check what PadWrite does?) + p->xoff = 0; +} From 5d18d477e36eb41c4f3088a0652a6bec76eec630 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 18:19:32 -0500 Subject: [PATCH 117/487] Implemented uiDrawCaret() on OS X. --- darwin/drawtext.m | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c0da5c2f..3fb12e54 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -327,3 +327,25 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ return CTLineGetOffsetForStringIndex(lr, pos, NULL); } + +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + NSColor *cc; + CGFloat cr, cg, cb, ca; + + // Interface Builder sets this as the insertion point color for a NSTextView by default + cc = [NSColor controlTextColor]; + // the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception + cc = [cc colorUsingColorSpace:[NSColorSpace sRGBColorSpace]]; + [cc getRed:&cr green:&cg blue:&cb alpha:&ca]; + p->r = cr; + p->g = cg; + p->b = cb; + p->a = ca; + // both cc and the controlTextColor it was made from will be autoreleased since they aren't new or init calls + // TODO disabled carets have some blending applied... + + // okay there doesn't seem to be any defined metrics for these, argh... + p->width = 1; + p->xoff = 0; +} From 5354aa52624b0b6d43bd333a9f6875858de45a53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 19:08:31 -0500 Subject: [PATCH 118/487] And cleaned hittest.c up. --- examples/drawtext/hittest.c | 48 ++++--------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 1d836efa..8fe78ab7 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -2,7 +2,6 @@ #include "drawtext.h" // TODO have a ligature -// TODO allow clicking on the end of a line to put the caret there // TODO the hiding and showing does not work properly on GTK+ static const char *text = @@ -70,24 +69,11 @@ static uiDrawBrush fillBrushes[4] = { }, }; -// TODO this should be const -static uiDrawStrokeParams strokeParams = { - .Cap = uiDrawLineCapFlat, - .Join = uiDrawLineJoinMiter, - .Thickness = 2, - .MiterLimit = uiDrawDefaultMiterLimit, - .Dashes = NULL, - .NumDashes = 0, - .DashPhase = 0, -}; - static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; uiDrawTextLayoutLineMetrics m; - uiDrawBrush brush; - double caretX; // only clip the text, not the guides uiDrawSave(p->Context); @@ -111,25 +97,8 @@ static void draw(uiAreaDrawParams *p) caretLine = uiDrawTextLayoutNumLines(layout) - 1; caretPos = uiAttributedStringLen(attrstr); } -#if 0 - caretX = uiDrawTextLayoutByteLocationInLine(layout, - caretPos, caretLine); - uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); - path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); - uiDrawPathLineTo(path, margins + caretX, margins + m.Y + m.Height); - uiDrawPathEnd(path); - brush.Type = uiDrawBrushTypeSolid; - brush.R = 0.0; - brush.G = 0.0; - brush.B = 1.0; - brush.A = 1.0; - uiDrawStroke(p->Context, path, &brush, &strokeParams); - uiDrawFreePath(path); -#else uiDrawCaret(p->Context, margins, margins, layout, caretPos, &caretLine); -#endif if (uiCheckboxChecked(showLineBounds)) { int i, n; @@ -154,7 +123,6 @@ static void draw(uiAreaDrawParams *p) static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; -// uiDrawTextLayoutHitTestResult res; char labelText[128]; if (e->Down != 1) @@ -165,24 +133,16 @@ static void mouse(uiAreaMouseEvent *e) e->AreaWidth - 2 * margins); uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, -// &res); &caretPos, &caretLine); -// caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); uiDrawFreeTextLayout(layout); // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option - sprintf(labelText, "pos %d line %d",// x %g",// x position %s y position %s", - (int) caretPos, caretLine);//, caretX); -/* (int) (res.Pos), res.Line, - positions[res.XPosition], - positions[res.YPosition]); -*/ uiLabelSetText(caretLabel, labelText); + sprintf(labelText, "pos %d line %d", + (int) caretPos, caretLine); + uiLabelSetText(caretLabel, labelText); -/* cursorX = res.CaretX; - cursorY = res.CaretY; - cursorHeight = res.CaretHeight; -*/ redraw(); + redraw(); } static struct example hitTestExample; From 5f05ebbffe7cc5bdff988f4be8e6277b1eedf8dd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 19:22:25 -0500 Subject: [PATCH 119/487] Expanded the hittest example by handling keyboard input. I should probably rename it to The Caret and Graphemes. --- examples/drawtext/basic.c | 1 + examples/drawtext/drawtext.h | 1 + examples/drawtext/hittest.c | 38 +++++++++++++++++++++++++++++++++++- examples/drawtext/main.c | 3 ++- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index c40cf36c..e1aaca02 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -254,6 +254,7 @@ struct example *mkBasicExample(void) basicExample.panel = uiControl(panel); basicExample.draw = draw; basicExample.mouse = NULL; + basicExample.key = NULL; attrstr = uiNewAttributedString(text); diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index c82383eb..f14ee485 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -8,6 +8,7 @@ struct example { uiControl *panel; void (*draw)(uiAreaDrawParams *p); void (*mouse)(uiAreaMouseEvent *e); + int (*key)(uiAreaKeyEvent *e); // TODO key? }; diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 8fe78ab7..cbd0bd90 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -35,7 +35,6 @@ static uiCheckbox *showLineBounds; static int caretLine = -1; static size_t caretPos; -//static double caretX; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -136,6 +135,7 @@ static void mouse(uiAreaMouseEvent *e) &caretPos, &caretLine); uiDrawFreeTextLayout(layout); + // TODO move this into the draw handler so it is reflected by keyboard-based position changes // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option sprintf(labelText, "pos %d line %d", @@ -145,6 +145,41 @@ static void mouse(uiAreaMouseEvent *e) redraw(); } +static int key(uiAreaKeyEvent *e) +{ + size_t grapheme; + + if (e->Up) + return 0; + if (e->Key != 0) + return 0; + switch (e->ExtKey) { + case uiExtKeyUp: + // TODO + return 1; + case uiExtKeyDown: + // TODO + return 1; + case uiExtKeyLeft: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == 0) + return 0; + grapheme--; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + case uiExtKeyRight: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == uiAttributedStringNumGraphemes(attrstr)) + return 0; + grapheme++; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + } + return 0; +} + static struct example hitTestExample; // TODO share? @@ -174,6 +209,7 @@ struct example *mkHitTestExample(void) hitTestExample.panel = uiControl(panel); hitTestExample.draw = draw; hitTestExample.mouse = mouse; + hitTestExample.key = key; attrstr = uiNewAttributedString(text); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 552b2f6b..cc28acd3 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -47,7 +47,8 @@ static void handlerDragBroken(uiAreaHandler *ah, uiArea *a) static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) { - // reject all keys + if (examples[curExample]->key != NULL) + return examples[curExample]->key(e); return 0; } From 6acb10a4fcfee465a4a74ba5f28311017da28369 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 19:29:07 -0500 Subject: [PATCH 120/487] Fixed hanging errors in uiDrawCaret(). --- common/drawtext.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 4c9dd200..c55227f1 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -10,24 +10,21 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, uiDrawPath *path; uiDrawBrush brush; + if (*line < 0) + *line = 0; + if (*line > (uiDrawTextLayoutNumLines(layout) - 1)) + *line = (uiDrawTextLayoutNumLines(layout) - 1); + // TODO cap pos xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); - if (xoff < 0) { + while (xoff < 0) { size_t start, end; - int incr; - if (*line > (uiDrawTextLayoutNumLines(layout) - 1)) { - *line = (uiDrawTextLayoutNumLines(layout) - 1); - incr = -1; - } else { - uiDrawTextLayoutLineByteRange(layout, *line, &start, &end); - incr = 1; - if (end < pos) - incr = -1; - } - while (xoff < 0) { - *line += incr; - xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); - } + uiDrawTextLayoutLineByteRange(layout, *line, &start, &end); + if (end < pos) // too far up + (*line)++; + else + (*line)--; + xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); } uiDrawTextLayoutLineGetMetrics(layout, *line, &m); From 5a1c7338412a003a6bd0238be7bee84d040e3561 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 20:37:05 -0500 Subject: [PATCH 121/487] Something is wrong with the Windows code... --- common/drawtext.c | 1 + windows/graphemes.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index c55227f1..cbfe3ccb 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -2,6 +2,7 @@ #include "../ui.h" #include "uipriv.h" +// TODO figure out how to make this work on GTK+ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line) { double xoff; diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 2b93fab4..16f2fd82 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -21,12 +21,11 @@ static HRESULT itemize(WCHAR *s, size_t len, SCRIPT_ITEM **out, int *outn) int n; HRESULT hr; - // make sure these are zero-initialized to avoid mangling the text - ZeroMemory(&sc, sizeof (SCRIPT_CONTROL)); - ZeroMemory(&ss, sizeof (SCRIPT_STATE)); - maxItems = len + 2; for (;;) { + // make sure these are zero-initialized to avoid mangling the text + ZeroMemory(&sc, sizeof (SCRIPT_CONTROL)); + ZeroMemory(&ss, sizeof (SCRIPT_STATE)); items = new SCRIPT_ITEM[maxItems + 1]; hr = ScriptItemize(s, len, maxItems, @@ -101,8 +100,9 @@ struct graphemes *graphemes(void *s, size_t len) delete[] logattr; } // and handle the last item for the end of the string - *pGTP++ = items[nItems].iCharPos; - *pPTG++ = pGTP - g->graphemesToPoints; + *pGTP = items[nItems].iCharPos; + *pPTG = pGTP - g->graphemesToPoints; + // TODO is any of the above broken?... also for all platforms, are the last few bytes not covered? delete[] items; return g; From c4b6149ec25b7c66beb43d14116a3a58c8e91f62 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 22:12:37 -0500 Subject: [PATCH 122/487] Gave up and rewrote Windows graphemes.cpp to use CharNextW() instead of Uniscribe, since I can't manage Uniscribe memory properly, it seems. The CharNextW() bug is relatively painless to work around anyway. Next commit will drop Uniscribe from the headers and build system. --- windows/graphemes.cpp | 108 ++++++++++++------------------------------ 1 file changed, 29 insertions(+), 79 deletions(-) diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 16f2fd82..256c3a07 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -1,109 +1,59 @@ // 25 may 2016 #include "uipriv_windows.hpp" -// We could use CharNext() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). -// So let's use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html) -// See also http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/ for more details. - -// TODO the DirectWrite equivalent appears to be https://msdn.microsoft.com/en-us/library/windows/desktop/dd316625(v=vs.85).aspx but is somehow somewhat more complicated to use than Uniscribe is! maybe the PadWrite sample uses it? or should we just keep using Uniscribe? +// We could use CharNextW() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). +// We could also use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html, http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/), but its rules for buffer sizes is convoluted. +// Let's just deal with the CharNextW() bug. int graphemesTakesUTF16(void) { return 1; } -static HRESULT itemize(WCHAR *s, size_t len, SCRIPT_ITEM **out, int *outn) -{ - SCRIPT_CONTROL sc; - SCRIPT_STATE ss; - SCRIPT_ITEM *items; - size_t maxItems; - int n; - HRESULT hr; - - maxItems = len + 2; - for (;;) { - // make sure these are zero-initialized to avoid mangling the text - ZeroMemory(&sc, sizeof (SCRIPT_CONTROL)); - ZeroMemory(&ss, sizeof (SCRIPT_STATE)); - items = new SCRIPT_ITEM[maxItems + 1]; - hr = ScriptItemize(s, len, - maxItems, - &sc, &ss, - items, &n); - if (hr == S_OK) - break; - // otherwise either an error or not enough room - delete[] items; - if (hr != E_OUTOFMEMORY) - return hr; - maxItems *= 2; // add some more and try again - } - - *out = items; - *outn = n; - return S_OK; -} - struct graphemes *graphemes(void *s, size_t len) { struct graphemes *g; - WCHAR *str = (WCHAR *) s; - SCRIPT_ITEM *items; - int nItems; - int curItemIndex; - int nCharsInCurItem; + WCHAR *str; size_t *pPTG, *pGTP; - HRESULT hr; g = uiNew(struct graphemes); - hr = itemize(str, len, &items, &nItems); - if (hr != S_OK) - logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); - g->len = nItems; + g->len = 0; + str = (WCHAR *) s; + while (*str != L'\0') { + g->len++; + str = CharNextW(str); + // no need to worry about surrogates if we're just counting + } + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - // note that there are actually nItems + 1 elements in items - // items[nItems] is the grapheme one past the end g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); pPTG = g->pointsToGraphemes; pGTP = g->graphemesToPoints; - for (curItemIndex = 0; curItemIndex < nItems; curItemIndex++) { - SCRIPT_ITEM *curItem, *nextItem; - SCRIPT_LOGATTR *logattr; - size_t *curGTP; - int i; + str = (WCHAR *) s; + while (*str != L'\0') { + WCHAR *next, *p; + ptrdiff_t nextoff; - curItem = items + curItemIndex; - nextItem = curItem + 1; + // as part of the bug, we need to make sure we only call CharNextW() on low halves, otherwise it'll return the same low half forever + nextoff = 0; + if (IS_HIGH_SURROGATE(*str)) + nextoff = 1; + next = CharNextW(str + nextoff); + if (IS_LOW_SURROGATE(*next)) + next--; - nCharsInCurItem = nextItem->iCharPos - curItem->iCharPos; + *pGTP = pPTG - g->pointsToGraphemes; + for (p = str; p < next; p++) + *pPTG++ = pGTP - g->graphemesToPoints; + pGTP++; - logattr = new SCRIPT_LOGATTR[nCharsInCurItem]; - hr = ScriptBreak(str + curItem->iCharPos, nCharsInCurItem, - &(curItem->a), logattr); - if (hr != S_OK) - logHRESULT(L"error breaking string for finding grapheme cluster boundaries", hr); - - // TODO can we merge these loops somehow? - curGTP = pGTP; - for (i = 0; i < nCharsInCurItem; i++) - if (logattr[i].fCharStop != 0) - *pGTP++ = curItem->iCharPos + i; - for (i = 0; i < nCharsInCurItem; i++) { - *pPTG++ = curGTP - g->graphemesToPoints; - if (logattr[i].fCharStop != 0) - curGTP++; - } - - delete[] logattr; + str = next; } // and handle the last item for the end of the string - *pGTP = items[nItems].iCharPos; + *pGTP = pPTG - g->pointsToGraphemes; *pPTG = pGTP - g->graphemesToPoints; - // TODO is any of the above broken?... also for all platforms, are the last few bytes not covered? - delete[] items; return g; } From 230d23765a3a76f068e222c8be45bd44b095ba27 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 22:22:47 -0500 Subject: [PATCH 123/487] Removed Uniscribe from the Windows build process and header file list. Also more TODOs. Next major goal: reinstating the font button. --- examples/drawtext/hittest.c | 1 + windows/CMakeLists.txt | 3 +-- windows/winapi.hpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index cbd0bd90..2393e65b 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,6 +3,7 @@ // TODO have a ligature // TODO the hiding and showing does not work properly on GTK+ +// TODO using the arrow keys allows us to walk back to the end of the line; IIRC arrow keys shouldn't do that static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 5b1232ed..a9551776 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -76,10 +76,9 @@ macro(_handle_static) COMMENT "Copying libui.res") endmacro() -# notice that usp10 comes before gdi32 # TODO prune this list set(_LIBUI_LIBS - user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid + user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid PARENT_SCOPE) if(NOT MSVC) diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 86aba5d7..a540809c 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include From 49094bb340c710d0dabe7a08abd9711d509943c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 00:25:07 -0500 Subject: [PATCH 124/487] Merged uiFontButton back in, with an updated API that works directly with uiDrawFontDescriptors. --- ui.h | 30 ------------------------------ ui_attrstr.h | 8 ++++++++ 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/ui.h b/ui.h index ddc9bb63..922d0625 100644 --- a/ui.h +++ b/ui.h @@ -478,26 +478,6 @@ _UI_EXTERN void uiDrawRestore(uiDrawContext *c); // TODO merge back in #include "ui_attrstr.h" -// TODO -#if 0 -struct uiDrawTextFontMetrics { - double Ascent; - double Descent; - double Leading; - // TODO do these two mean the same across all platforms? - double UnderlinePos; - double UnderlineThickness; -}; - -_UI_EXTERN uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc); -_UI_EXTERN void uiDrawFreeTextFont(uiDrawTextFont *font); -_UI_EXTERN uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font); -_UI_EXTERN void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc); -// TODO make copy with given attributes methods? -// TODO yuck this name -_UI_EXTERN void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics); -#endif - _UI_ENUM(uiModifiers) { uiModifierCtrl = 1 << 0, uiModifierAlt = 1 << 1, @@ -577,16 +557,6 @@ struct uiAreaKeyEvent { int Up; }; -#if 0 /* TODO */ -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// TODO document this returns a new font -_UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b); -// TOOD SetFont, mechanics -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -_UI_EXTERN uiFontButton *uiNewFontButton(void); -#endif - typedef struct uiColorButton uiColorButton; #define uiColorButton(this) ((uiColorButton *) (this)) _UI_EXTERN void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a); diff --git a/ui_attrstr.h b/ui_attrstr.h index 052f067d..9fb81513 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -164,3 +164,11 @@ _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking + +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// TODO document this returns a new font +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); +// TOOD SetFont, mechanics +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +_UI_EXTERN uiFontButton *uiNewFontButton(void); From bebaf72de3694877e29ece8aa2197aba2be0b39f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 00:53:52 -0500 Subject: [PATCH 125/487] Started putting uiFontButton back in on OS X. Just a bunch of boilerplate for now, namely the function to take a CTFontDesciptorRef and convert it into a uiDrawFontDescriptor. --- darwin/CMakeLists.txt | 2 +- darwin/fontmatch.m | 82 ++++++++++++++++++++++++++++++++++++++++++ darwin/uipriv_darwin.h | 8 +---- 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 34368c06..0fce3838 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -17,7 +17,7 @@ list(APPEND _LIBUI_SOURCES darwin/drawtext.m darwin/editablecombo.m darwin/entry.m -#TODO darwin/fontbutton.m + darwin/fontbutton.m darwin/fontmatch.m darwin/form.m darwin/graphemes.m diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 04761ef4..821855a7 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -253,3 +253,85 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) fd->Italic, stretchesToCTWidths[fd->Stretch]); } + +// TODO deduplicate this from italicCloseness() +static uiDrawTextItalic italicFromCTItalic(CTFontDescriptorRef desc, CFDictionaryRef traits) +{ + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work + SInt32 s; + CFStringRef styleName; + BOOL isOblique; + + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { + // TODO + } + symbolic = (CTFontSymbolicTraits) s; + // Get Rule; do not release cfnum + if ((symbolic & kCTFontItalicTrait) == 0) + return uiDrawTextItalicNormal; + + // Okay, now we know it's either Italic or Oblique + // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + // TODO is styleName guaranteed? + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return uiDrawTextItalicOblique; + return uiDrawTextItalicItalic; +} + +void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) +{ + CFStringRef cffamily; + CFDictionaryRef traits; + double ctweight, ctstretch; + int wc; + uiDrawTextStretch stretch; + + cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontFamilyNameAttribute); + if (cffamily == NULL) { + // TODO + } + // TODO normalize this by adding a uiDarwinCFStringToText() + uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); + CFRelease(cffamily); + + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontTraitsAttribute); + if (traits == NULL) { + // TODO + } + ctweight = doubleAttr(traits, kCTFontWeightTrait); + uidesc->Italic = italicFromCTItalic(ctdesc, traits); + ctstretch = doubleAttr(traits, kCTFontWidthTrait); + CFRelease(traits); + + // TODO make sure this is correct + for (wc = 0; wc < 10; wc++) + if (ctweight >= weightsToCTWeights[wc]) + if (ctweight < weightsToCTWeights[wc + 1]) + break; + uidesc->Weight = ((ctweight - weightsToCTWeights[wc]) / (weightsToCTWeights[wc + 1] - weightsToCTWeights[wc])) * 100; + uidesc->Weight += wc * 100; + + // TODO is this correct? + for (stretch = uiDrawTextStretchUltraCondensed; stretch < uiDrawTextStretchUltraExpanded; stretch++) + if (ctstretch <= stretchesToCTWidth[stretch]) + break; + uidesc->Stretch = stretch; +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0a15e3bf..336f5bcf 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -109,15 +109,9 @@ extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); // fontbutton.m -#if 0 /* TODO */ extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); extern void setupFontPanel(void); -#else -static inline BOOL fontButtonInhibitSendAction(SEL sel, id from, id to) { return NO; } -static inline BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) { return NO; } -static inline void setupFontPanel(void) {} -#endif // colorbutton.m extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); @@ -148,4 +142,4 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd); +extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); From 67949d79aa71a2710861d3c2d6cbb65de15f163a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 01:13:07 -0500 Subject: [PATCH 126/487] And re-added the uiFontButton on OS X and added one to the hit-test example. --- darwin/fontbutton.m | 17 ++++++++++++----- darwin/fontmatch.m | 6 +++--- examples/drawtext/hittest.c | 28 +++++++++++++++++++++++----- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index 22bc6465..433f261f 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -11,7 +11,7 @@ - (void)activateFontButton; - (void)deactivateFontButton:(BOOL)activatingAnother; - (void)deactivateOnClose:(NSNotification *)note; -- (uiDrawTextFont *)libuiFont; +- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc; @end // only one may be active at one time @@ -138,9 +138,16 @@ struct uiFontButton { NSFontPanelCollectionModeMask; } -- (uiDrawTextFont *)libuiFont +- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc { - return mkTextFontFromNSFont(self->libui_font); + CTFontRef ctfont; + CTFontDescriptorRef ctdesc; + + ctfont = (CTFontRef) (self->libui_font); + ctdesc = CTFontCopyFontDescriptor(ctfont); + fontdescFromCTFontDescriptor(ctdesc, uidesc); + CFRelease(ctdesc); + uidesc->Size = CTFontGetSize(ctfont); } @end @@ -192,9 +199,9 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -uiDrawTextFont *uiFontButtonFont(uiFontButton *b) +void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - return [b->button libuiFont]; + [b->button getfontdesc:desc]; } void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 821855a7..e1660c8b 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -304,7 +304,7 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript int wc; uiDrawTextStretch stretch; - cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontFamilyNameAttribute); + cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontFamilyNameAttribute); if (cffamily == NULL) { // TODO } @@ -312,7 +312,7 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontTraitsAttribute); + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontTraitsAttribute); if (traits == NULL) { // TODO } @@ -331,7 +331,7 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript // TODO is this correct? for (stretch = uiDrawTextStretchUltraCondensed; stretch < uiDrawTextStretchUltraExpanded; stretch++) - if (ctstretch <= stretchesToCTWidth[stretch]) + if (ctstretch <= stretchesToCTWidths[stretch]) break; uidesc->Stretch = stretch; } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 2393e65b..2b80be5e 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -31,8 +31,10 @@ static uiAttributedString *attrstr; #define margins 10 static uiBox *panel; +static uiBox *vbox; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; +static uiFontButton *fontButton; static int caretLine = -1; static size_t caretPos; @@ -189,22 +191,38 @@ static void checkboxChecked(uiCheckbox *c, void *data) redraw(); } -static uiCheckbox *newCheckbox(const char *text) +static void changeFont(uiFontButton *b, void *data) +{ + // TODO free old font name + // TODO rename defaultFont + uiFontButtonFont(fontButton, &defaultFont); + // TODO dump the new font + redraw(); +} + +// TODO share? +static uiCheckbox *newCheckbox(uiBox *box, const char *text) { uiCheckbox *c; c = uiNewCheckbox(text); uiCheckboxOnToggled(c, checkboxChecked, NULL); - uiBoxAppend(panel, uiControl(c), 0); + uiBoxAppend(box, uiControl(c), 0); return c; } struct example *mkHitTestExample(void) { - panel = uiNewVerticalBox(); + panel = uiNewHorizontalBox(); + vbox = uiNewVerticalBox(); + uiBoxAppend(panel, uiControl(vbox), 1); caretLabel = uiNewLabel("Caret information is shown here"); - uiBoxAppend(panel, uiControl(caretLabel), 0); - showLineBounds = newCheckbox("Show Line Bounds (for debugging metrics)"); + uiBoxAppend(vbox, uiControl(caretLabel), 0); + showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)"); + fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, changeFont, NULL); + // TODO set the font button to the current defaultFont + uiBoxAppend(panel, uiControl(fontButton), 0); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); From a014eb27e65ec3555f4d82c2a7207722cbee79e4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 14:22:23 -0500 Subject: [PATCH 127/487] More TODO completion in the drawtext example. --- examples/drawtext/hittest.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 2b80be5e..8e44e5c0 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -193,10 +193,16 @@ static void checkboxChecked(uiCheckbox *c, void *data) static void changeFont(uiFontButton *b, void *data) { - // TODO free old font name + if (defaultFont.Family != fontFamily) + uiFreeText(defaultFont.Family); // TODO rename defaultFont uiFontButtonFont(fontButton, &defaultFont); - // TODO dump the new font + printf("{\n\tfamily: %s\n\tsize: %g\n\tweight: %d\n\titalic: %d\n\tstretch: %d\n}\n", + defaultFont.Family, + defaultFont.Size, + (int) (defaultFont.Weight), + (int) (defaultFont.Italic), + (int) (defaultFont.Stretch)); redraw(); } From 1a8f7ad405024bc507d761e5c5d3c46bb6d78dcc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 14:45:58 -0500 Subject: [PATCH 128/487] Reimplemented uiFontButton on GTK+. --- unix/CMakeLists.txt | 2 +- unix/drawtext.c | 22 ++++++++++++++++++++++ unix/fontbutton.c | 12 +++++------- unix/uipriv_unix.h | 9 +++------ 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 1684e2ce..9300bcb7 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -22,7 +22,7 @@ list(APPEND _LIBUI_SOURCES unix/drawtext.c unix/editablecombo.c unix/entry.c -# unix/fontbutton.c + unix/fontbutton.c unix/form.c unix/future.c unix/graphemes.c diff --git a/unix/drawtext.c b/unix/drawtext.c index 175403ad..4cbe3c71 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -280,3 +280,25 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) p->xoff = xoff; p->width = width; } + +// TODO split this and the other font description stuff into their own file? +void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) +{ + PangoStyle pitalic; + PangoStretch pstretch; + + uidesc->Family = uiUnixStrdupText(pango_font_description_get_family(pdesc)); + pitalic = pango_font_description_get_style(pdesc); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = pango_font_description_get_weight(pdesc); + pstretch = pango_font_description_get_stretch(pdesc); + // absolute size does not matter because, as above, 1 device unit == 1 cairo point + uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (pangoItalics[uidesc->Italic] == pitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (pangoStretches[uidesc->Stretch] == pstretch) + break; +} diff --git a/unix/fontbutton.c b/unix/fontbutton.c index f8047e08..9a2552b1 100644 --- a/unix/fontbutton.c +++ b/unix/fontbutton.c @@ -26,16 +26,14 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -uiDrawTextFont *uiFontButtonFont(uiFontButton *b) +void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - PangoFont *f; - PangoFontDescription *desc; + PangoFontDescription *pdesc; - desc = gtk_font_chooser_get_font_desc(b->fc); - f = pangoDescToPangoFont(desc); + pdesc = gtk_font_chooser_get_font_desc(b->fc); + fontdescFromPangoFontDescription(pdesc, desc); // desc is transfer-full and thus is a copy - pango_font_description_free(desc); - return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref + pango_font_description_free(pdesc); } void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 3764f8ef..aaae8b34 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -45,12 +45,6 @@ extern void childSetMargined(struct child *c, int margined); extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); extern void freeContext(uiDrawContext *); -// drawtext.c -#if 0 /* TODO */ -extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); -extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); -#endif - // image.c /*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); @@ -62,3 +56,6 @@ extern GtkCellRenderer *newCellRendererButton(void); extern void loadFutures(void); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); + +// drawtext.c +extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); From dca92d507ee2e99d779d3f828070c47a0db1a121 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 16:55:30 -0500 Subject: [PATCH 129/487] And readded uiFontButton on Windows. Woo! Now we have to deal with styles. --- windows/CMakeLists.txt | 4 ++-- windows/_uipriv_migrate.hpp | 27 --------------------------- windows/drawtext.cpp | 19 +++++++++++++++++++ windows/dwrite.cpp | 4 ---- windows/fontbutton.cpp | 8 ++++---- windows/fontdialog.cpp | 1 + windows/uipriv_windows.hpp | 3 +++ 7 files changed, 29 insertions(+), 37 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index a9551776..5c401d06 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -26,8 +26,8 @@ list(APPEND _LIBUI_SOURCES windows/editablecombo.cpp windows/entry.cpp windows/events.cpp -# windows/fontbutton.cpp -# windows/fontdialog.cpp + windows/fontbutton.cpp + windows/fontdialog.cpp windows/form.cpp windows/graphemes.cpp windows/grid.cpp diff --git a/windows/_uipriv_migrate.hpp b/windows/_uipriv_migrate.hpp index 11b737d1..b9c365cc 100644 --- a/windows/_uipriv_migrate.hpp +++ b/windows/_uipriv_migrate.hpp @@ -14,13 +14,9 @@ extern uiDrawContext *newContext(ID2D1RenderTarget *); extern void freeContext(uiDrawContext *); // dwrite.cpp -#ifdef __cplusplus extern IDWriteFactory *dwfactory; -#endif extern HRESULT initDrawText(void); extern void uninitDrawText(void); -#if 0 /* TODO */ -#ifdef __cplusplus struct fontCollection { IDWriteFontCollection *fonts; WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; @@ -30,29 +26,8 @@ extern fontCollection *loadFontCollection(void); extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); extern void fontCollectionFree(fontCollection *fc); extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); -#endif -#endif - -// drawtext.cpp -#if 0 /* TODO */ -#ifdef __cplusplus -extern uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL copyFamily, double size); -struct dwriteAttr { - uiDrawTextWeight weight; - uiDrawTextItalic italic; - uiDrawTextStretch stretch; - DWRITE_FONT_WEIGHT dweight; - DWRITE_FONT_STYLE ditalic; - DWRITE_FONT_STRETCH dstretch; -}; -extern void attrToDWriteAttr(struct dwriteAttr *attr); -extern void dwriteAttrToAttr(struct dwriteAttr *attr); -#endif -#endif // fontdialog.cpp -#if 0 /* TODO */ -#ifdef __cplusplus struct fontDialogParams { IDWriteFont *font; double size; @@ -63,5 +38,3 @@ extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); extern void loadInitialFontDialogParams(struct fontDialogParams *params); extern void destroyFontDialogParams(struct fontDialogParams *params); extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); -#endif -#endif diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index b7a5ccdf..4049bbb1 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -401,3 +401,22 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) // and there doesn't seem to be this either... (TODO check what PadWrite does?) p->xoff = 0; } + +// TODO split this and the above related font matching code into a separate file? +void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) +{ + DWRITE_FONT_STYLE dwitalic; + DWRITE_FONT_STRETCH dwstretch; + + dwitalic = font->GetStyle(); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); + dwstretch = font->GetStretch(); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (dwriteItalics[uidesc->Italic] == dwitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (dwriteStretches[uidesc->Stretch] == dwstretch) + break; +} diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 6e9e5835..2e9ce928 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -17,8 +17,6 @@ void uninitDrawText(void) dwfactory->Release(); } -#if 0 /* TODO */ - fontCollection *loadFontCollection(void) { fontCollection *fc; @@ -88,5 +86,3 @@ void fontCollectionFree(fontCollection *fc) fc->fonts->Release(); uiFree(fc); } - -#endif diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index d2d4dabf..ab9fbe73 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -86,11 +86,11 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -uiDrawTextFont *uiFontButtonFont(uiFontButton *b) +void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - // we don't own b->params.font; we have to add a reference - // we don't own b->params.familyName either; we have to copy it - return mkTextFont(b->params.font, TRUE, b->params.familyName, TRUE, b->params.size); + fontdescFromIDWriteFont(b->params.font, desc); + desc->Family = toUTF8(b->params.familyName); + desc->Size = b->params.size; } void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index 603a17db..d7fa91e9 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -320,6 +320,7 @@ static void sizeEdited(struct fontDialog *f) wsize = windowText(f->sizeCombobox); // this is what the Choose Font dialog does; it swallows errors while the real ChooseFont() is not lenient (and only checks on OK) size = wcstod(wsize, NULL); + // TODO free wsize? I forget already if (size <= 0) // don't change on invalid size return; f->curSize = size; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 2354bfd7..f1de95cf 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -159,3 +159,6 @@ extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); // draw.cpp extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); + +// drawtext.cpp +extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); From 491ec3ae499df0b1f48941ae5fb651195718725a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 17:10:59 -0500 Subject: [PATCH 130/487] Actually let's do paragraph alignment first. Haven't tested hit-test with that just yet. --- ui_attrstr.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9fb81513..8e95be98 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -86,8 +86,22 @@ struct uiDrawFontDescriptor { }; typedef struct uiDrawTextLayout uiDrawTextLayout; +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +_UI_ENUM(uiDrawTextLayoutAlign) { + uiDrawTextLayoutAlignLeft, + uiDrawTextLayoutAlignCenter, + uiDrawTextLayoutAlignRight, +}; + +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiDrawFontDescriptor *DefaultFont; + double Width; + uiDrawTextLayoutAlign Align; +}; + struct uiDrawTextLayoutLineMetrics { // This describes the overall bounding box of the line. // TODO figure out if X is correct regardless of both alignment and writing direction @@ -128,7 +142,7 @@ struct uiDrawTextLayoutLineMetrics { // - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) // - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) // - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); From 8a64a1dfb01ed386819b8a11dc62ad6fbd2ca17d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 19:47:20 -0500 Subject: [PATCH 131/487] Made the OS X code and the example program use the new layout stuff. --- darwin/drawtext.m | 12 ++++++------ examples/drawtext/basic.c | 11 +++++++---- examples/drawtext/hittest.c | 14 ++++++++------ ui_attrstr.h | 1 + 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 3fb12e54..e7a59164 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -155,7 +155,7 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize return metrics; } -uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; CGFloat cgwidth; @@ -163,10 +163,10 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto CGRect rect; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(s, defaultFont); + tl->attrstr = attrstrToCoreFoundation(p->String, p->DefaultFont); range.location = 0; range.length = CFAttributedStringGetLength(tl->attrstr); - tl->width = width; + tl->width = p->Width; // TODO CTFrameProgression for RTL/LTR // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing @@ -175,7 +175,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO } - cgwidth = (CGFloat) width; + cgwidth = (CGFloat) (tl->width); if (cgwidth < 0) cgwidth = CGFLOAT_MAX; // TODO these seem to be floor()'d or truncated? @@ -204,8 +204,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); // and finally copy the UTF-8/UTF-16 conversion tables - tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); return tl; } diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index e1aaca02..1ddbbe6c 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -18,7 +18,7 @@ static const char *text = "and important." ""; static char fontFamily[] = "Palatino"; -// TODO should be const; look at constructor function +// TODO should be const; look at constructor function? static uiDrawFontDescriptor defaultFont = { .Family = fontFamily, .Size = 18, @@ -27,6 +27,7 @@ static uiDrawFontDescriptor defaultFont = { .Stretch = uiDrawTextStretchNormal, }; static uiAttributedString *attrstr; +static uiDrawTextLayoutParams params; #define margins 10 @@ -141,9 +142,8 @@ static void draw(uiAreaDrawParams *p) uiDrawFreePath(path); #endif - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - p->AreaWidth - 2 * margins); + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); uiDrawText(p->Context, layout, margins, margins); uiDrawRestore(p->Context); @@ -257,6 +257,9 @@ struct example *mkBasicExample(void) basicExample.key = NULL; attrstr = uiNewAttributedString(text); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; return &basicExample; } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 8e44e5c0..3b6125b0 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -27,6 +27,7 @@ static uiDrawFontDescriptor defaultFont = { .Stretch = uiDrawTextStretchNormal, }; static uiAttributedString *attrstr; +static uiDrawTextLayoutParams params; #define margins 10 @@ -88,9 +89,8 @@ static void draw(uiAreaDrawParams *p) uiDrawClip(p->Context, path); uiDrawFreePath(path); - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - p->AreaWidth - 2 * margins); + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); uiDrawText(p->Context, layout, margins, margins); uiDrawRestore(p->Context); @@ -130,9 +130,8 @@ static void mouse(uiAreaMouseEvent *e) if (e->Down != 1) return; - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - e->AreaWidth - 2 * margins); + params.Width = e->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, &caretPos, &caretLine); @@ -237,6 +236,9 @@ struct example *mkHitTestExample(void) hitTestExample.key = key; attrstr = uiNewAttributedString(text); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; return &hitTestExample; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 8e95be98..63aed4c0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -89,6 +89,7 @@ typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +// TODO drop the Layout from this? _UI_ENUM(uiDrawTextLayoutAlign) { uiDrawTextLayoutAlignLeft, uiDrawTextLayoutAlignCenter, From bd66e70452224c4528b27f9b6a79122b063987ff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 19:56:26 -0500 Subject: [PATCH 132/487] And added the alignment flag to the example program. --- examples/drawtext/hittest.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 3b6125b0..03012891 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -36,6 +36,7 @@ static uiBox *vbox; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; static uiFontButton *fontButton; +static uiCombobox *textAlign; static int caretLine = -1; static size_t caretPos; @@ -205,6 +206,13 @@ static void changeFont(uiFontButton *b, void *data) redraw(); } +static void changeTextAlign(uiCombobox *c, void *data) +{ + // note the order of the items added below + params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + redraw(); +} + // TODO share? static uiCheckbox *newCheckbox(uiBox *box, const char *text) { @@ -220,14 +228,24 @@ struct example *mkHitTestExample(void) { panel = uiNewHorizontalBox(); vbox = uiNewVerticalBox(); + // TODO the second vbox causes this not to stretch at least on OS X uiBoxAppend(panel, uiControl(vbox), 1); caretLabel = uiNewLabel("Caret information is shown here"); uiBoxAppend(vbox, uiControl(caretLabel), 0); showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)"); + vbox = uiNewVerticalBox(); + uiBoxAppend(panel, uiControl(vbox), 0); fontButton = uiNewFontButton(); uiFontButtonOnChanged(fontButton, changeFont, NULL); // TODO set the font button to the current defaultFont - uiBoxAppend(panel, uiControl(fontButton), 0); + uiBoxAppend(vbox, uiControl(fontButton), 0); + textAlign = uiNewCombobox(); + // note that these are in the order in the enum + uiComboboxAppend(textAlign, "Left"); + uiComboboxAppend(textAlign, "Center"); + uiComboboxAppend(textAlign, "Right"); + uiComboboxOnSelected(textAlign, changeTextAlign, NULL); + uiBoxAppend(vbox, uiControl(textAlign), 0); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); From 44f8409b8c4b5f3b0bc0810da604923df7cc3436 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 21:10:16 -0500 Subject: [PATCH 133/487] And implemented the alignment stuff on OS X. --- darwin/drawtext.m | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index e7a59164..bae7326d 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -46,27 +46,55 @@ static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) return font; } -static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) +static const CTTextAlignment ctaligns[] = { + [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, + [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, + [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, +}; + +static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) +{ + CTParagraphStyleRef ps; + CTParagraphStyleSetting settings[16]; + size_t nSettings = 0; + + settings[nSettings].spec = kCTParagraphStyleSpecifierAlignment; + settings[nSettings].valueSize = sizeof (CTTextAlignment); + settings[nSettings].value = ctaligns + p->Align; + nSettings++; + + ps = CTParagraphStyleCreate(settings, nSettings); + if (ps == NULL) { + // TODO + } + return ps; +} + +static CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; CTFontRef defaultCTFont; + CTParagraphStyleRef ps; CFAttributedStringRef base; CFMutableAttributedStringRef mas; - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); if (cfstr == NULL) { // TODO } - defaultAttrs = CFDictionaryCreateMutable(NULL, 1, + defaultAttrs = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (defaultAttrs == NULL) { // TODO } - defaultCTFont = fontdescToCTFont(defaultFont); + defaultCTFont = fontdescToCTFont(p->DefaultFont); CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); CFRelease(defaultCTFont); + ps = mkParagraphStyle(p); + CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); + CFRelease(ps); base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); if (base == NULL) { @@ -163,7 +191,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) CGRect rect; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(p->String, p->DefaultFont); + tl->attrstr = attrstrToCoreFoundation(p); range.location = 0; range.length = CFAttributedStringGetLength(tl->attrstr); tl->width = p->Width; From 210c4507cadf81f9e5521ef6674cd2f323b41756 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 21:25:41 -0500 Subject: [PATCH 134/487] Implemented uiDrawTextLayoutParams and alignment on GTK+. --- unix/drawtext.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 4cbe3c71..abc9aa65 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -90,7 +90,13 @@ static void computeLineMetrics(uiDrawTextLayout *tl) pango_layout_iter_free(iter); } -uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +static const PangoAlignment pangoAligns[] = { + [uiDrawTextLayoutAlignLeft] = PANGO_ALIGN_LEFT, + [uiDrawTextLayoutAlignCenter] = PANGO_ALIGN_CENTER, + [uiDrawTextLayoutAlignRight] = PANGO_ALIGN_RIGHT, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; PangoContext *context; @@ -106,29 +112,31 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto g_object_unref(context); // this is safe; pango_layout_set_text() copies the string - pango_layout_set_text(tl->layout, uiAttributedStringString(s), -1); + pango_layout_set_text(tl->layout, uiAttributedStringString(p->String), -1); desc = pango_font_description_new(); - pango_font_description_set_family(desc, defaultFont->Family); - pango_font_description_set_style(desc, pangoItalics[defaultFont->Italic]); + pango_font_description_set_family(desc, p->DefaultFont->Family); + pango_font_description_set_style(desc, pangoItalics[p->DefaultFont->Italic]); // for the most part, pango weights correlate to ours // the differences: // - Book — libui: 350, Pango: 380 // - Ultra Heavy — libui: 950, Pango: 1000 // TODO figure out what to do about this misalignment - pango_font_description_set_weight(desc, defaultFont->Weight); - pango_font_description_set_stretch(desc, pangoStretches[defaultFont->Stretch]); + pango_font_description_set_weight(desc, p->DefaultFont->Weight); + pango_font_description_set_stretch(desc, pangoStretches[p->DefaultFont->Stretch]); // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double - pango_font_description_set_size(desc, pango_units_from_double(defaultFont->Size)); + pango_font_description_set_size(desc, pango_units_from_double(p->DefaultFont->Size)); pango_layout_set_font_description(tl->layout, desc); // this is safe; the description is copied pango_font_description_free(desc); - pangoWidth = cairoToPango(width); - if (width < 0) + pangoWidth = cairoToPango(p->Width); + if (p->Width < 0) pangoWidth = -1; pango_layout_set_width(tl->layout, pangoWidth); + pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); + // TODO attributes tl->nLines = pango_layout_get_line_count(tl->layout); From c4a97792ea347d167de13eb273c5242d03ab5cc8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 21:54:06 -0500 Subject: [PATCH 135/487] And implemented the new stuff on Windows. --- examples/drawtext/hittest.c | 2 +- windows/drawtext.cpp | 32 +++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 03012891..48d2c409 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -1,7 +1,7 @@ // 20 january 2017 #include "drawtext.h" -// TODO have a ligature +// TODO double-check ligatures // TODO the hiding and showing does not work properly on GTK+ // TODO using the arrow keys allows us to walk back to the end of the line; IIRC arrow keys shouldn't do that diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 4049bbb1..da512fd9 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -129,7 +129,14 @@ static void computeLineInfo(uiDrawTextLayout *tl) delete[] dlm; } -uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteAligns = { + { uiDrawTextLayoutAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, + { uiDrawTextLayoutAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, + { uiDrawTextLayoutAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; WCHAR *wDefaultFamily; @@ -139,7 +146,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl = uiNew(uiDrawTextLayout); - wDefaultFamily = toUTF16(defaultFont->Family); + wDefaultFamily = toUTF16(p->DefaultFont->Family); hr = dwfactory->CreateTextFormat( wDefaultFamily, NULL, // for the most part, DirectWrite weights correlate to ours @@ -147,19 +154,22 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // - Minimum — libui: 0, DirectWrite: 1 // - Maximum — libui: 1000, DirectWrite: 999 // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) - (DWRITE_FONT_WEIGHT) (defaultFont->Weight), - dwriteItalics[defaultFont->Italic], - dwriteStretches[defaultFont->Stretch], - pointSizeToDWriteSize(defaultFont->Size), + (DWRITE_FONT_WEIGHT) (p->DefaultFont->Weight), + dwriteItalics[p->DefaultFont->Italic], + dwriteStretches[p->DefaultFont->Stretch], + pointSizeToDWriteSize(p->DefaultFont->Size), // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx // TODO use the current locale? L"", &(tl->format)); if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); + hr = tl->format->SetTextAlignment(dwriteAligns[p->Align]); + if (hr != S_OK) + logHRESULT(L"error applying text layout alignment", hr); hr = dwfactory->CreateTextLayout( - (const WCHAR *) attrstrUTF16(s), attrstrUTF16Len(s), + (const WCHAR *) attrstrUTF16(p->String), attrstrUTF16Len(p->String), tl->format, // FLOAT is float, not double, so this should work... TODO FLT_MAX, FLT_MAX, @@ -170,8 +180,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // and set the width // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway) wrap = DWRITE_WORD_WRAPPING_WRAP; - maxWidth = (FLOAT) width; - if (width < 0) { + maxWidth = (FLOAT) (p->Width); + if (p->Width < 0) { // TODO is this wrapping juggling even necessary? wrap = DWRITE_WORD_WRAPPING_NO_WRAP; // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe @@ -187,8 +197,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto computeLineInfo(tl); // and finally copy the UTF-8/UTF-16 index conversion tables - tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); // TODO can/should this be moved elsewhere? uiFree(wDefaultFamily); From de638f3e76d64c882f94754f93c4e211b922e607 Mon Sep 17 00:00:00 2001 From: nopara73 Date: Sun, 12 Feb 2017 12:55:43 +0900 Subject: [PATCH 136/487] Add .NET Core bindings --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 01275c0e..a9ddce2e 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,8 @@ Other people have made bindings to other languages: Language | Bindings --- | --- -C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) +C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) +C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) From 32a0284edcb3bedfd31f19d9184ac06137611a3b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 23:19:30 -0500 Subject: [PATCH 137/487] Started work on actual attributed text. This includes the beginnings of an attributed text example. Now to implement. --- common/attrstr.c | 5 ++ examples/CMakeLists.txt | 1 + examples/drawtext/attributes.c | 159 +++++++++++++++++++++++++++++++++ examples/drawtext/drawtext.h | 3 + examples/drawtext/main.c | 5 ++ ui_attrstr.h | 11 ++- 6 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 examples/drawtext/attributes.c diff --git a/common/attrstr.c b/common/attrstr.c index f62c42f2..93e4e8f4 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -297,6 +297,11 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) return pos; } +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end) +{ + attrlistInsertAttribute(s->attrs, type, value, start, end); +} + // TODO introduce an iterator? void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3fda1b82..64c7098c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,6 +32,7 @@ if(NOT WIN32) endif() _add_example(drawtext + drawtext/attributes.c drawtext/basic.c drawtext/hittest.c drawtext/main.c diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c new file mode 100644 index 00000000..b66635df --- /dev/null +++ b/examples/drawtext/attributes.c @@ -0,0 +1,159 @@ +// 11 february 2017 +#include "drawtext.h" + +static uiAttributedString *attrstr; + +static void setupAttributedString(void) +{ + size_t start, end; + const char *next; + + attrstr = uiNewAttributedString("uiAttributedString isn't just for plain text! It supports "); + + next = "multiple fonts"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeFamily, + (uintptr_t) "Courier New", + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple sizes"; +} + +static char fontFamily[] = "Times New Roman"; +// TODO should be const; look at constructor function? +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 12, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiDrawTextLayoutParams params; + +#define margins 10 + +static uiBox *panel; +static uiCheckbox *showExtents; +static uiCheckbox *showLineBounds; +static uiCheckbox *showLineGuides; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + uiDrawBrush b; + + b.Type = uiDrawBrushTypeSolid; + + // only clip the text, not the guides + uiDrawSave(p->Context); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); + uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + if (uiCheckboxChecked(showLineBounds)) { + uiDrawTextLayoutLineMetrics m; + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + } + + uiDrawFreeTextLayout(layout); +} + +static struct example attributesExample; + +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static uiCheckbox *newCheckbox(const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(panel, uiControl(c), 0); + return c; +} + +struct example *mkAttributesExample(void) +{ + panel = uiNewVerticalBox(); + showLineBounds = newCheckbox("Show Line Bounds"); + + attributesExample.name = "Attributed Text"; + attributesExample.panel = uiControl(panel); + attributesExample.draw = draw; + attributesExample.mouse = NULL; + attributesExample.key = NULL; + + setupAttributedString(); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; + + return &attributesExample; +} diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index f14ee485..0ac4cb36 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -20,3 +20,6 @@ extern struct example *mkBasicExample(void); // hittest.c extern struct example *mkHitTestExample(void); + +// attributes.c +extern struct example *mkAttributesExample(void); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index cc28acd3..822382d0 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -110,6 +110,11 @@ int main(void) uiControlHide(examples[n]->panel); uiBoxAppend(box, examples[n]->panel, 0); n++; + examples[n] = mkAttributesExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); + uiBoxAppend(box, examples[n]->panel, 0); + n++; // and set things up for the initial state uiComboboxSetSelected(exampleList, 0); uiComboboxOnSelected(exampleList, onExampleChanged, NULL); diff --git a/ui_attrstr.h b/ui_attrstr.h index 63aed4c0..87eac1cf 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,15 +1,19 @@ typedef struct uiAttributedString uiAttributedString; _UI_ENUM(uiAttribute) { - // TODO uiAttributeFamily, - // TODO uiAttributeSize, + // TODO once we allow loading fonts in memory we can't use just one pointer anymore + uiAttributeFamily, + // TODO no guarantee this can fit in a uintptr_t; we need a better way to store these... + uiAttributeSize, uiAttributeWeight, + uiAttributeItalic, + uiAttributeStretch, // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, }; -typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); +typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -35,6 +39,7 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; From 5aaac84d5537dfe7f521825fdd3f2c45f90b4de3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 01:05:27 -0500 Subject: [PATCH 138/487] Implemented uiAttributeFamily on OS X. ATTRIBUTES WORK!!! --- darwin/CMakeLists.txt | 1 + darwin/attrstr.m | 158 +++++++++++++++++++++++++++++++++++++++++ darwin/drawtext.m | 77 -------------------- darwin/uipriv_darwin.h | 3 + 4 files changed, 162 insertions(+), 77 deletions(-) create mode 100644 darwin/attrstr.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 0fce3838..7d9ead75 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -4,6 +4,7 @@ list(APPEND _LIBUI_SOURCES darwin/alloc.m darwin/area.m darwin/areaevents.m + darwin/attrstr.m darwin/autolayout.m darwin/box.m darwin/button.m diff --git a/darwin/attrstr.m b/darwin/attrstr.m new file mode 100644 index 00000000..49f0d20a --- /dev/null +++ b/darwin/attrstr.m @@ -0,0 +1,158 @@ +// 12 february 2017 +#import "uipriv_darwin.h" + +// unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute +// TODO see if we could use NSAttributedString? +// TODO consider renaming this struct and the fep variable(s) +struct foreachParams { + CFMutableAttributedStringRef mas; + NSMutableDictionary *converted; + uiDrawFontDescriptor *defaultFont; +}; + +static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) +{ + size_t i; + NSNumber *n; + uiDrawFontDescriptor *new; + + for (i = start; i < end; i++) { + n = [NSNumber numberWithInteger:i]; + if ([p->converted objectForKey:n] != nil) + continue; + new = uiNew(uiDrawFontDescriptor); + *new = *(p->defaultFont); + [p->converted setObject:[NSValue valueWithPointer:new] forKey:n]; + } +} + +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiDrawFontDescriptor *desc)) +{ + size_t i; + NSNumber *n; + NSValue *v; + + for (i = start; i < end; i++) { + n = [NSNumber numberWithInteger:i]; + v = (NSValue *) [p->converted objectForKey:n]; + adj((uiDrawFontDescriptor *) [v pointerValue]); + } +} + +static int processAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; + + start = attrstrUTF8ToUTF16(s, start); + end = attrstrUTF8ToUTF16(s, end); + switch (type) { + case uiAttributeFamily: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Family = (char *) value; + }); + break; + // TODO + } + return 0; +} + +static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +{ + CTFontDescriptorRef desc; + CTFontRef font; + + desc = fontdescToCTFontDescriptor(fd); + font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); + CFRelease(desc); // TODO correct? + return font; +} + +static void applyAndFreeFontAttributes(struct foreachParams *p) +{ + [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { + uiDrawFontDescriptor *desc; + CTFontRef font; + CFRange range; + + desc = (uiDrawFontDescriptor *) [val pointerValue]; + font = fontdescToCTFont(desc); + range.location = [key integerValue]; + range.length = 1; + CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); + CFRelease(font); + uiFree(desc); + }]; +} + +static const CTTextAlignment ctaligns[] = { + [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, + [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, + [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, +}; + +static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) +{ + CTParagraphStyleRef ps; + CTParagraphStyleSetting settings[16]; + size_t nSettings = 0; + + settings[nSettings].spec = kCTParagraphStyleSpecifierAlignment; + settings[nSettings].valueSize = sizeof (CTTextAlignment); + settings[nSettings].value = ctaligns + p->Align; + nSettings++; + + ps = CTParagraphStyleCreate(settings, nSettings); + if (ps == NULL) { + // TODO + } + return ps; +} + +CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) +{ + CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; + CTParagraphStyleRef ps; + CFAttributedStringRef base; + CFMutableAttributedStringRef mas; + struct foreachParams fep; + + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); + if (cfstr == NULL) { + // TODO + } + defaultAttrs = CFDictionaryCreateMutable(NULL, 0, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (defaultAttrs == NULL) { + // TODO + } + defaultCTFont = fontdescToCTFont(p->DefaultFont); + CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); + CFRelease(defaultCTFont); + ps = mkParagraphStyle(p); + CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); + CFRelease(ps); + + base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); + if (base == NULL) { + // TODO + } + CFRelease(cfstr); + CFRelease(defaultAttrs); + mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); + CFRelease(base); + + CFAttributedStringBeginEditing(mas); + fep.mas = mas; + fep.converted = [NSMutableDictionary new]; + fep.defaultFont = p->DefaultFont; + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeFontAttributes(&fep); + [fep.converted release]; + CFAttributedStringEndEditing(mas); + + return mas; +} diff --git a/darwin/drawtext.m b/darwin/drawtext.m index bae7326d..9fbe5bed 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -35,83 +35,6 @@ struct uiDrawTextLayout { size_t nUTF16; }; -static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) -{ - CTFontDescriptorRef desc; - CTFontRef font; - - desc = fontdescToCTFontDescriptor(fd); - font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); - CFRelease(desc); // TODO correct? - return font; -} - -static const CTTextAlignment ctaligns[] = { - [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, - [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, - [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, -}; - -static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) -{ - CTParagraphStyleRef ps; - CTParagraphStyleSetting settings[16]; - size_t nSettings = 0; - - settings[nSettings].spec = kCTParagraphStyleSpecifierAlignment; - settings[nSettings].valueSize = sizeof (CTTextAlignment); - settings[nSettings].value = ctaligns + p->Align; - nSettings++; - - ps = CTParagraphStyleCreate(settings, nSettings); - if (ps == NULL) { - // TODO - } - return ps; -} - -static CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) -{ - CFStringRef cfstr; - CFMutableDictionaryRef defaultAttrs; - CTFontRef defaultCTFont; - CTParagraphStyleRef ps; - CFAttributedStringRef base; - CFMutableAttributedStringRef mas; - - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); - if (cfstr == NULL) { - // TODO - } - defaultAttrs = CFDictionaryCreateMutable(NULL, 0, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (defaultAttrs == NULL) { - // TODO - } - defaultCTFont = fontdescToCTFont(p->DefaultFont); - CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); - CFRelease(defaultCTFont); - ps = mkParagraphStyle(p); - CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); - CFRelease(ps); - - base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); - if (base == NULL) { - // TODO - } - CFRelease(cfstr); - CFRelease(defaultAttrs); - mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); - CFRelease(base); - - CFAttributedStringBeginEditing(mas); - // TODO copy in the attributes - CFAttributedStringEndEditing(mas); - - return mas; -} - // TODO this is wrong for our hit-test example's multiple combining character example static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 336f5bcf..c9ab1c34 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -143,3 +143,6 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); + +// attrstr.m +extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p); From 1c1b16a20658e8cb8f7ca1a20c4961ca204f686b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 13:41:52 -0500 Subject: [PATCH 139/487] More attributes. Beyond this point I'd need to either redefine the way attributes are specified or make more header macros. --- darwin/attrstr.m | 25 +++++++++++++++++++ examples/drawtext/attributes.c | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 49f0d20a..d1298944 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -2,6 +2,7 @@ #import "uipriv_darwin.h" // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute +// TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) struct foreachParams { @@ -52,6 +53,30 @@ static int processAttribute(uiAttributedString *s, uiAttribute type, uintptr_t v desc->Family = (char *) value; }); break; + case uiAttributeSize: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Size = *((double *) value); + }); + break; + case uiAttributeWeight: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Weight = (uiDrawTextWeight) value; + }); + break; + case uiAttributeItalic: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Italic = (uiDrawTextItalic) value; + }); + break; + case uiAttributeStretch: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Stretch = (uiDrawTextStretch) value; + }); + break; // TODO } return 0; diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index b66635df..428c1275 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -22,6 +22,51 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); next = "multiple sizes"; + static double eighteen = 18; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeSize, + (uintptr_t) (&eighteen), + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple weights"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeWeight, + (uintptr_t) uiDrawTextWeightBold, + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple italics"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeItalic, + (uintptr_t) uiDrawTextItalicItalic, + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple stretches"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeStretch, + (uintptr_t) uiDrawTextStretchCondensed, + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple TODO"; } static char fontFamily[] = "Times New Roman"; From 261dd4851afbaa099935552851ec1c68f5ea9eb6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 14:11:25 -0500 Subject: [PATCH 140/487] Changed the representation of an attribute type/value pair to allow more type safety and expressability. --- common/attrlist.c | 41 +++++++++++++++++++++------------- common/attrstr.c | 4 ++-- common/uipriv.h | 2 +- darwin/attrstr.m | 14 ++++++------ examples/drawtext/attributes.c | 36 +++++++++++++---------------- ui_attrstr.h | 11 +++++++-- 6 files changed, 61 insertions(+), 47 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index f65120f7..5afb862f 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -12,8 +12,7 @@ The linked list is not a ring; alist->fist->prev == NULL and alist->last->next = */ struct attr { - uiAttribute type; - uintptr_t val; + uiAttributeSpec spec; size_t start; size_t end; struct attr *prev; @@ -178,8 +177,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t // we'll need to split the attribute into two b = uiNew(struct attr); - b->type = a->type; - b->val = a->val; + b->spec = a->spec; b->start = end; b->end = a->end; *tail = b; @@ -223,8 +221,7 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a return NULL; b = uiNew(struct attr); - b->type = a->type; - b->val = a->val; + b->spec = a->spec; b->start = at; b->end = a->end; @@ -287,7 +284,24 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) +{ + if (attr->spec.Type != spec->Type) + return 0; + switch (attr->spec.Type) { + case uiAttributeFamily: + // TODO should we start copying these strings? + return strcmp((char *) (attr->spec.Value), (char *) (spec->Value)) == 0; + case uiAttributeSize: + // TODO use a closest match? + return attr->spec.Double == spec->Double; + // TODO + } + // handles the rest + return attr->spec.Value == spec->Value; +} + +void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end) { struct attr *a; struct attr *before; @@ -309,7 +323,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t goto next; // should we split this? - if (before->type != type) + if (before->spec.Type != spec->Type) goto next; lstart = start; lend = end; @@ -317,10 +331,8 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t goto next; // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything - // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? - // TODO will this cause problems with fonts? // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? - if (before->val == val) { + if (specsIdentical(before, spec)) { attrGrow(alist, before, start, end); return; } @@ -335,8 +347,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t // if we got here, we know we have to add the attribute before before a = uiNew(struct attr); - a->type = type; - a->val = val; + a->spec = *spec; a->start = start; a->end = end; attrInsertBefore(alist, a, before); @@ -498,7 +509,7 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st // and at this point we're done, so break; } - if (a->type != type) + if (a->spec.Type != type) goto next; lstart = start; lend = end; @@ -587,7 +598,7 @@ void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributed for (a = alist->first; a != NULL; a = a->next) // TODO document this // TODO should this be return 0 to break? - if ((*f)(s, a->type, a->val, a->start, a->end, data)) + if ((*f)(s, &(a->spec), a->start, a->end, data)) break; } diff --git a/common/attrstr.c b/common/attrstr.c index 93e4e8f4..9c12a532 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -297,9 +297,9 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) return pos; } -void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end) +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) { - attrlistInsertAttribute(s->attrs, type, value, start, end); + attrlistInsertAttribute(s->attrs, spec, start, end); } // TODO introduce an iterator? diff --git a/common/uipriv.h b/common/uipriv.h index 73c7eeb0..9d0dc8e6 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -78,7 +78,7 @@ extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); // attrlist.c struct attrlist; -extern void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end); +extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index d1298944..16f7fb92 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -40,41 +40,41 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, } } -static int processAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data) +static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); - switch (type) { + switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Family = (char *) value; + desc->Family = (char *) (spec->Value); }); break; case uiAttributeSize: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Size = *((double *) value); + desc->Size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Weight = (uiDrawTextWeight) value; + desc->Weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Italic = (uiDrawTextItalic) value; + desc->Italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Stretch = (uiDrawTextStretch) value; + desc->Stretch = (uiDrawTextStretch) (spec->Value); }); break; // TODO diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 428c1275..c2a91889 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -5,6 +5,7 @@ static uiAttributedString *attrstr; static void setupAttributedString(void) { + uiAttributeSpec spec; size_t start, end; const char *next; @@ -14,22 +15,20 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeFamily, - (uintptr_t) "Courier New", - start, end); + spec.Type = uiAttributeFamily; + spec.Value = (uintptr_t) "Courier New"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); next = "multiple sizes"; - static double eighteen = 18; start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSize; + spec.Double = 18; uiAttributedStringSetAttribute(attrstr, - uiAttributeSize, - (uintptr_t) (&eighteen), - start, end); + &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -37,10 +36,9 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeWeight, - (uintptr_t) uiDrawTextWeightBold, - start, end); + spec.Type = uiAttributeWeight; + spec.Value = (uintptr_t) uiDrawTextWeightBold; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -48,10 +46,9 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeItalic, - (uintptr_t) uiDrawTextItalicItalic, - start, end); + spec.Type = uiAttributeItalic; + spec.Value = (uintptr_t) uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -59,10 +56,9 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeStretch, - (uintptr_t) uiDrawTextStretchCondensed, - start, end); + spec.Type = uiAttributeStretch; + spec.Value = (uintptr_t) uiDrawTextStretchCondensed; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/ui_attrstr.h b/ui_attrstr.h index 87eac1cf..096bcc4e 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,4 +1,5 @@ typedef struct uiAttributedString uiAttributedString; +typedef struct uiAttributeSpec uiAttributeSpec; _UI_ENUM(uiAttribute) { // TODO once we allow loading fonts in memory we can't use just one pointer anymore @@ -13,7 +14,13 @@ _UI_ENUM(uiAttribute) { // TODO uiAttributeCustom, }; -typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); +struct uiAttributeSpec { + uiAttribute Type; + uintptr_t Value; + double Double; +}; + +typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -39,7 +46,7 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end); +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; From 44f24fc90063653183235b48e14de3d4ec0b3671 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 20:27:47 -0500 Subject: [PATCH 141/487] Added the foreground color attribute. Considering making the background color a background BRUSH attribute instead... --- common/attrlist.c | 6 ++++++ darwin/attrstr.m | 20 ++++++++++++++++++++ darwin/draw.m | 4 ++++ examples/drawtext/attributes.c | 14 ++++++++++++++ ui_attrstr.h | 9 ++++++--- 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 5afb862f..5c75da58 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -295,6 +295,12 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) case uiAttributeSize: // TODO use a closest match? return attr->spec.Double == spec->Double; + case uiAttributeColor: + // TODO use a closest match? + return attr->spec.R == spec->R && + attr->spec.G == spec->G && + attr->spec.B == spec->B && + attr->spec.A == spec->A; // TODO } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 16f7fb92..e5d3c84e 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -43,9 +43,15 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; + CFRange range; + CGColorSpaceRef colorspace; + CGColorRef color; + CGFloat components[4]; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); + range.location = start; + range.length = end - start; switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); @@ -77,6 +83,20 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t desc->Stretch = (uiDrawTextStretch) (spec->Value); }); break; + case uiAttributeColor: + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (colorspace == NULL) { + // TODO + } + components[0] = spec->R; + components[1] = spec->G; + components[2] = spec->B; + components[3] = spec->A; + color = CGColorCreate(colorspace, components); + CFRelease(colorspace); + CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); + CFRelease(color); + break; // TODO } return 0; diff --git a/darwin/draw.m b/darwin/draw.m index d9aadede..b4be6e7f 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -218,6 +218,10 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) // gradients need a color space // for consistency with windows, use sRGB colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (colorspace == NULL) { + // TODO + } + // TODO add NULL check to other uses of CGColorSpace // make the gradient colors = uiAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]"); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c2a91889..d5c47058 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -62,6 +62,20 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple colors"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeColor; + // Direct2D "Crimson" (#DC143C) + spec.R = 0.8627450980392156; + spec.G = 0.0784313725490196; + spec.B = 0.2352941176470588; + spec.A = 0.75; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 096bcc4e..62a82f77 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -2,13 +2,12 @@ typedef struct uiAttributedString uiAttributedString; typedef struct uiAttributeSpec uiAttributeSpec; _UI_ENUM(uiAttribute) { - // TODO once we allow loading fonts in memory we can't use just one pointer anymore uiAttributeFamily, - // TODO no guarantee this can fit in a uintptr_t; we need a better way to store these... - uiAttributeSize, + uiAttributeSize, // use Double uiAttributeWeight, uiAttributeItalic, uiAttributeStretch, + uiAttributeColor, // use R, G, B, A // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, @@ -18,6 +17,10 @@ struct uiAttributeSpec { uiAttribute Type; uintptr_t Value; double Double; + double R; + double G; + double B; + double A; }; typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); From b2cd5ef851a7101f13fe3dff438ddbbdbd4d009f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 01:22:59 -0500 Subject: [PATCH 142/487] Wrote code to draw the background of text. --- common/drawtext.c | 38 ++++++++++++++++++++++++++++++++++++++ common/uipriv.h | 1 + 2 files changed, 39 insertions(+) diff --git a/common/drawtext.c b/common/drawtext.c index cbfe3ccb..9cc04966 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -49,3 +49,41 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, uiDrawRestore(c); } + +void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection) +{ + int line, nLines; + size_t lstart, lend; + double layoutwid, layoutht; + + uiDrawTextLayoutExtents(layout, &layoutwid, &layoutht); + nLines = uiDrawTextLayoutNumLines(layout); + for (line = 0; line < nLines; line++) { + uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend); + if (start >= lstart && start < lend) + break; + } + while (end - start > 0) { + uiDrawTextLayoutLineMetrics m; + double startx, endx; + uiDrawPath *path; + + if (lend > end) // don't cross lines + lend = end; + startx = uiDrawTextLayoutByteLocationInLine(layout, line, start); + // TODO explain this + endx = layoutwid; + if (!isSelection || end <= lend) + endx = uiDrawTextLayoutByteLocationInLine(layout, line, end); + uiDrawTextLayoutLineGetMetrics(layout, line, &m); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, + x + startx, y + m.Y, + endx - startx, m.Height); + uiDrawPathEnd(path); + uiDrawFill(c, path, brush); + uiDrawFreePath(path); + line++; + start += lend - start; + } +} diff --git a/common/uipriv.h b/common/uipriv.h index 9d0dc8e6..553073f5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -99,6 +99,7 @@ struct caretDrawParams { double width; }; extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); +extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); #ifdef __cplusplus } From 40c388e01d53bca38bfd4201209c53d3c40dbfa6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 01:28:46 -0500 Subject: [PATCH 143/487] Whoops, just realized I typo'd. --- common/drawtext.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 9cc04966..3e657a7d 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -71,10 +71,10 @@ void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout * if (lend > end) // don't cross lines lend = end; startx = uiDrawTextLayoutByteLocationInLine(layout, line, start); - // TODO explain this + // TODO explain this; note the use of start with lend endx = layoutwid; if (!isSelection || end <= lend) - endx = uiDrawTextLayoutByteLocationInLine(layout, line, end); + endx = uiDrawTextLayoutByteLocationInLine(layout, line, lend); uiDrawTextLayoutLineGetMetrics(layout, line, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, @@ -84,6 +84,6 @@ void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout * uiDrawFill(c, path, brush); uiDrawFreePath(path); line++; - start += lend - start; + start = lend; } } From 9eba197fd19872f92123a0ad660396720836149a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 01:44:18 -0500 Subject: [PATCH 144/487] Adjusted the example for backgrounds. Nope brushes isn't gonna work; absolute positioning of gradients is a problem. --- common/attrlist.c | 1 + examples/drawtext/attributes.c | 14 ++++++++++++++ ui_attrstr.h | 1 + 3 files changed, 16 insertions(+) diff --git a/common/attrlist.c b/common/attrlist.c index 5c75da58..9a785dd3 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -296,6 +296,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) // TODO use a closest match? return attr->spec.Double == spec->Double; case uiAttributeColor: + case uiAttributeBackground: // TODO use a closest match? return attr->spec.R == spec->R && attr->spec.G == spec->G && diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index d5c47058..63e2e762 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -76,6 +76,20 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple backgrounds"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeBackground; + // Direct2D "Peach Puff" (#FFDAB9) + spec.R = 1.0; + spec.G = 0.85490196078431372; + spec.B = 0.7254901960784313; + spec.A = 0.5; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 62a82f77..4fa90aea 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -8,6 +8,7 @@ _UI_ENUM(uiAttribute) { uiAttributeItalic, uiAttributeStretch, uiAttributeColor, // use R, G, B, A + uiAttributeBackground, // use R, G, B, A // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, From 1c238bf85ba677b7b0a0a059c437902d81ba8b2f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 02:10:39 -0500 Subject: [PATCH 145/487] And implemented uiAttributeBackground on OS X. Not sure what else to add besides the feature variants... --- common/drawtext.c | 5 +++-- darwin/attrstr.m | 29 ++++++++++++++++++++++++++++- darwin/drawtext.m | 10 +++++++++- darwin/uipriv_darwin.h | 3 ++- examples/drawtext/attributes.c | 1 + 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 3e657a7d..938e1aeb 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -68,13 +68,14 @@ void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout * double startx, endx; uiDrawPath *path; + uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend); if (lend > end) // don't cross lines lend = end; - startx = uiDrawTextLayoutByteLocationInLine(layout, line, start); + startx = uiDrawTextLayoutByteLocationInLine(layout, start, line); // TODO explain this; note the use of start with lend endx = layoutwid; if (!isSelection || end <= lend) - endx = uiDrawTextLayoutByteLocationInLine(layout, line, lend); + endx = uiDrawTextLayoutByteLocationInLine(layout, lend, line); uiDrawTextLayoutLineGetMetrics(layout, line, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, diff --git a/darwin/attrstr.m b/darwin/attrstr.m index e5d3c84e..c383ab59 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -9,6 +9,7 @@ struct foreachParams { CFMutableAttributedStringRef mas; NSMutableDictionary *converted; uiDrawFontDescriptor *defaultFont; + NSMutableArray *backgroundBlocks; }; static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -40,6 +41,20 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, } } +static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) +{ + return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = r; + brush.G = g; + brush.B = b; + brush.A = a; + drawTextBackground(c, x, y, layout, start, end, &brush, 0); + }); +} + static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -47,7 +62,11 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t CGColorSpaceRef colorspace; CGColorRef color; CGFloat components[4]; + size_t ostart, oend; + backgroundBlock block; + ostart = start; + oend = end; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); range.location = start; @@ -97,6 +116,12 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); CFRelease(color); break; + case uiAttributeBackground: + block = mkBackgroundBlock(ostart, oend, + spec->R, spec->G, spec->B, spec->A); + [p->backgroundBlocks addObject:block]; + Block_release(block); + break; // TODO } return 0; @@ -154,7 +179,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } -CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) +CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; @@ -194,10 +219,12 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) fep.mas = mas; fep.converted = [NSMutableDictionary new]; fep.defaultFont = p->DefaultFont; + fep.backgroundBlocks = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFontAttributes(&fep); [fep.converted release]; CFAttributedStringEndEditing(mas); + *backgroundBlocks = fep.backgroundBlocks; return mas; } diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 9fbe5bed..c5736bf9 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -28,6 +28,8 @@ struct uiDrawTextLayout { // we compute this once when first creating the layout uiDrawTextLayoutLineMetrics *lineMetrics; + NSArray *backgroundBlocks; + // for converting CFAttributedString indices from/to byte offsets size_t *u8tou16; size_t nUTF8; @@ -114,7 +116,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) CGRect rect; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(p); + tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks)); range.location = 0; range.length = CFAttributedStringGetLength(tl->attrstr); tl->width = p->Width; @@ -165,6 +167,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); uiFree(tl->u8tou16); + [tl->backgroundBlocks release]; uiFree(tl->lineMetrics); // TODO release tl->lines? CFRelease(tl->frame); @@ -177,8 +180,13 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) // TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { + backgroundBlock b; + CGContextSaveGState(c->c); + for (b in tl->backgroundBlocks) + b(c, tl, x, y); + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) // TODO how is this affected by a non-identity CTM? diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c9ab1c34..bf009522 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,4 +145,5 @@ extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m -extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p); +typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 63e2e762..543ba94c 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -82,6 +82,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeBackground; // Direct2D "Peach Puff" (#FFDAB9) + // TODO choose a darker color spec.R = 1.0; spec.G = 0.85490196078431372; spec.B = 0.7254901960784313; From ff986858f14cbe83317a1dd2c68d0121ab6afa7e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 10:24:12 -0500 Subject: [PATCH 146/487] Added vertical glyph forms. Next up is the rest of the various font features. --- common/attrlist.c | 2 ++ darwin/attrstr.m | 3 +++ examples/drawtext/attributes.c | 10 ++++++++++ ui_attrstr.h | 7 ++++--- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 9a785dd3..e4c8ad67 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -302,6 +302,8 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; + case uiAttributeVerticalForms: + return 1; // TODO } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c383ab59..cf6fde17 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -122,6 +122,9 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t [p->backgroundBlocks addObject:block]; Block_release(block); break; + case uiAttributeVerticalForms: + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); + break; // TODO } return 0; diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 543ba94c..7599dcfc 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -91,6 +91,16 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "vertical glyph forms"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 4fa90aea..6d6021d2 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -3,12 +3,13 @@ typedef struct uiAttributeSpec uiAttributeSpec; _UI_ENUM(uiAttribute) { uiAttributeFamily, - uiAttributeSize, // use Double + uiAttributeSize, // use Double uiAttributeWeight, uiAttributeItalic, uiAttributeStretch, - uiAttributeColor, // use R, G, B, A - uiAttributeBackground, // use R, G, B, A + uiAttributeColor, // use R, G, B, A + uiAttributeBackground, // use R, G, B, A + uiAttributeVerticalForms, // no parameter // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, From 9a19c65323a5d76fa02d4f1c17f2923d7fc4abff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 10:34:25 -0500 Subject: [PATCH 147/487] Prepared the OS X backend for adding feature attributes. Each feature will be a separate attribute for better composability. --- darwin/attrstr.m | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index cf6fde17..07663838 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -12,23 +12,32 @@ struct foreachParams { NSMutableArray *backgroundBlocks; }; +#define maxFeatures 32 + +struct fontParams { + uiDrawFontDescriptor desc; + uint16_t featureTypes[maxFeatures]; + uint16_t featureSpecifiers[maxFeatures]; + size_t nFeatures; +}; + static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; NSNumber *n; - uiDrawFontDescriptor *new; + struct fontParams *new; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; if ([p->converted objectForKey:n] != nil) continue; - new = uiNew(uiDrawFontDescriptor); - *new = *(p->defaultFont); + new = uiNew(struct fontParams); + new->desc = *(p->defaultFont); [p->converted setObject:[NSValue valueWithPointer:new] forKey:n]; } } -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiDrawFontDescriptor *desc)) +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(struct fontParams *fp)) { size_t i; NSNumber *n; @@ -37,7 +46,7 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; v = (NSValue *) [p->converted objectForKey:n]; - adj((uiDrawFontDescriptor *) [v pointerValue]); + adj((struct fontParams *) [v pointerValue]); } } @@ -74,32 +83,32 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Family = (char *) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Family = (char *) (spec->Value); }); break; case uiAttributeSize: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Size = spec->Double; + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Weight = (uiDrawTextWeight) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Italic = (uiDrawTextItalic) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Stretch = (uiDrawTextStretch) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); }); break; case uiAttributeColor: @@ -144,17 +153,17 @@ static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) static void applyAndFreeFontAttributes(struct foreachParams *p) { [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { - uiDrawFontDescriptor *desc; + struct fontParams *fp; CTFontRef font; CFRange range; - desc = (uiDrawFontDescriptor *) [val pointerValue]; - font = fontdescToCTFont(desc); + fp = (struct fontParams *) [val pointerValue]; + font = fontdescToCTFont(&(fp->desc)); range.location = [key integerValue]; range.length = 1; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); - uiFree(desc); + uiFree(fp); }]; } From 35a06e8540f076a4c4ed26cd685356e525cf6907 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 22:42:18 -0500 Subject: [PATCH 148/487] Changed uiAttributeVerticalForms into a boolean. This is how the other features might need to be implemented :/ --- common/attrlist.c | 9 ++++++++- darwin/attrstr.m | 5 ++++- examples/drawtext/attributes.c | 1 + ui_attrstr.h | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index e4c8ad67..1ca52537 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -284,6 +284,13 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } +static int boolsEqual(struct attr *attr, uiAttributeSpec *spec) +{ + if (attr->spec.Value == 0 && spec->Value == 0) + return 1; + return attr->spec.Value != 0 && spec->Value != 0; +} + static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) { if (attr->spec.Type != spec->Type) @@ -303,7 +310,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.B == spec->B && attr->spec.A == spec->A; case uiAttributeVerticalForms: - return 1; + return boolsEqual(attr, spec); // TODO } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 07663838..051bec16 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -132,7 +132,10 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t Block_release(block); break; case uiAttributeVerticalForms: - CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); + if (spec->Value != 0) + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); + else + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); break; // TODO } diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 7599dcfc..1aa74a72 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -96,6 +96,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeVerticalForms; + spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); diff --git a/ui_attrstr.h b/ui_attrstr.h index 6d6021d2..f5506fa0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -9,7 +9,7 @@ _UI_ENUM(uiAttribute) { uiAttributeStretch, uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A - uiAttributeVerticalForms, // no parameter + uiAttributeVerticalForms, // 0 = off, nonzero = 1 // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, From e3b1e033b2622c5dc0a8f3e9410090000896462c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 23:20:38 -0500 Subject: [PATCH 149/487] More TODOs. --- ui_attrstr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index f5506fa0..897c738b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -9,6 +9,7 @@ _UI_ENUM(uiAttribute) { uiAttributeStretch, uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A + // TODO rename to uiAttributeVertical? uiAttributeVerticalForms, // 0 = off, nonzero = 1 // TODO // TODO uiAttributeSystem, From fbae38ce2eb7ff69758256484b00fbe041d4d0ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 02:26:26 -0500 Subject: [PATCH 150/487] Started mapping out attributes for typographic features. I FORGOT THE OTHER ATTRIBUTES I NEED TO DO THOSE FIRST. --- ui_attrstr.h | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 897c738b..05d92857 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,6 +1,7 @@ typedef struct uiAttributedString uiAttributedString; typedef struct uiAttributeSpec uiAttributeSpec; +// Note: where you say "1 = on", any nonzero value means "on". (TODO) _UI_ENUM(uiAttribute) { uiAttributeFamily, uiAttributeSize, // use Double @@ -10,12 +11,166 @@ _UI_ENUM(uiAttribute) { uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A // TODO rename to uiAttributeVertical? - uiAttributeVerticalForms, // 0 = off, nonzero = 1 - // TODO + uiAttributeVerticalForms, // 0 = off, 1 = on + + // TODO underline and others + +#if 0 + + // These attributes represent typographic features. Each feature + // is a separate attribute, to make composition easier. The + // availability of for each attribute are defined by the font; the + // default values are defined by the font and/or by the OS. + // + // A note about features whose parameter is an enumeration: + // OS X defines typographic features using the AAT specification + // and converts to OpenType internally when needed, whereas + // other platforms use OpenType directly. OpenType is less + // precise about what each enumeration value means than AAT + // is, so enumeration values do not necessarily represent what + // OS X expects with all fonts. In cases where they do, libui + // provides an enumeration type to use. Otherwise, the AAT + // enumeration values are provided in comments for + // documentation purposes. + + // TODO kAllTypographicFeaturesType + + uiAttributeCommonLigatures, // 0 = off, 1 = on + uiAttributeRequiredLigatures, // 0 = off, 1 = on + uiAttributeRareLigatures, // 0 = off, 1 = on + uiAttributeLogoLigatures, // 0 = off, 1 = on + uiAttributeRebusPictureLigatures, // 0 = off, 1 = on + uiAttributeDipthrongLigatures, // 0 = off, 1 = on + uiAttributeSquaredLigatures, // 0 = off, 1 = on + // TODO rename + uiAttributeAbbreviatedSquared, // 0 = off, 1 = on + uiAttributeSymbolLigatures, // 0 = off, 1 = on + uiAttributeContextualLigatures, // 0 = off, 1 = on + uiAttributeHistoricalLigatures, // 0 = off, 1 = on + + uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all + + uiAttributeLetterCasing, // TODO + // kLetterCaseType + + uiAttributeLinguisticRearrangement, // 0 = off, 1 = on + + uiAttributeNumberSpacing, // TODO + // kNumberSpacingType + + // TODO kSmartSwashType + + // TODO kDiacriticsType + + // TODO kVerticalPositionType + + uiAttributeFractionForms, // enum uiAttributeFractionForm + + // TODO kOverlappingCharactersType + + // TODO kTypographicExtrasType + uiAttributeSlashedZero, // 0 = off, 1 = on + + // TODO kMathematicalExtrasType + uiAttributeMathematicalGreek, // 0 = off, 1 = on + + // TODO kOrnamentSetsType + + // TODO kCharacterAlternativesType + + // TODO kDesignComplexityType + + // TODO kStyleOptionsType + // TODO titling? + + // TODO kCharacterShapeType + // we could probably make the enum now + + uiAttributeNumberCase, // TODO + + // TODO kTextSpacingType + + // TODO kTransliterationType + + // AAT defines the following values: + // 0 = none + // 1 = box + // 2 = rounded box + // 3 = circle + // 4 = inverted circle + // 5 = parentheses + // 6 = period + // 7 = roman numeral + // 8 = diamond + // 9 = inverted box + // 10 = inverted rounded box + uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + + // TODO kKanaSpacingType + + // TODO kIdeographicSpacingType + + // TODO kUnicodeDecompositionType + + // TODO kRubyKanaType + + // TODO kCJKSymbolAlternativesType + + // TODO kIdeographicAlternativesType + + // TODO kCJKVerticalRomanPlacementType + + // TODO kItalicCJKRomanType + + // TODO kCaseSensitiveLayoutType + + // TODO kAlternateKanaType + + uiAttributeStylisticAlternative1, // 0 = off, 1 = on + uiAttributeStylisticAlternative2, // 0 = off, 1 = on + uiAttributeStylisticAlternative3, // 0 = off, 1 = on + uiAttributeStylisticAlternative4, // 0 = off, 1 = on + uiAttributeStylisticAlternative5, // 0 = off, 1 = on + uiAttributeStylisticAlternative6, // 0 = off, 1 = on + uiAttributeStylisticAlternative7, // 0 = off, 1 = on + uiAttributeStylisticAlternative8, // 0 = off, 1 = on + uiAttributeStylisticAlternative9, // 0 = off, 1 = on + uiAttributeStylisticAlternative10, // 0 = off, 1 = on + uiAttributeStylisticAlternative11, // 0 = off, 1 = on + uiAttributeStylisticAlternative12, // 0 = off, 1 = on + uiAttributeStylisticAlternative13, // 0 = off, 1 = on + uiAttributeStylisticAlternative14, // 0 = off, 1 = on + uiAttributeStylisticAlternative15, // 0 = off, 1 = on + uiAttributeStylisticAlternative16, // 0 = off, 1 = on + uiAttributeStylisticAlternative17, // 0 = off, 1 = on + uiAttributeStylisticAlternative18, // 0 = off, 1 = on + uiAttributeStylisticAlternative19, // 0 = off, 1 = on + uiAttributeStylisticAlternative20, // 0 = off, 1 = on + + // TODO kContextualAlternatesType + + // TODO kLowerCaseType + + // TODO kUpperCaseType + + // TODO kLanguageTagType? + + // TODO kCJKRomanSpacingType + +#endif + // TODO uiAttributeSystem, // TODO uiAttributeCustom, }; +_UI_ENUM(uiAttributeFractionForm) { + uiAttributeFractionFormNone, + uiAttributeFractionFormVertical, + uiAttributeFractionFormDiagonal, +}; + +// TODO number case + struct uiAttributeSpec { uiAttribute Type; uintptr_t Value; From 8aa82592509d859840166a94a8cf5b3c1232d649 Mon Sep 17 00:00:00 2001 From: nopara73 Date: Tue, 14 Feb 2017 16:35:19 +0900 Subject: [PATCH 151/487] SharpUI is .NET Core, too. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9ddce2e..3e5a3a58 100644 --- a/README.md +++ b/README.md @@ -147,8 +147,8 @@ Other people have made bindings to other languages: Language | Bindings --- | --- -C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) -C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI) +C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) +C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) From 9c1293736bb95d81e52a5587815b47eae6be2d60 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 02:53:21 -0500 Subject: [PATCH 152/487] Prepared the non-feature attributes based on the OS X list. --- ui_attrstr.h | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 05d92857..4c967254 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -13,7 +13,54 @@ _UI_ENUM(uiAttribute) { // TODO rename to uiAttributeVertical? uiAttributeVerticalForms, // 0 = off, 1 = on - // TODO underline and others + // TODO kerning amount + // OS X: kCTKernAttributeName + // > 0: farther (TODO from advance or standard kerning?) + // == 0: no kerning + // < 0: closer (TODO same) + // undefined: standard kerning + // Pango: pango_attr_letter_spacing_new() + // parameter meaning unspecified + // Windows: requires Platform Update, SetLetterSpacing() + // parameter meaning unspecified + + // TODO kCTLigatureAttributeName vs below + + // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? + + // TODO underline styles + // OS X: kCTUnderlineStyleAttributeName + // none, single, thick, or double + // styles: solid, dot, dash, dash dot, dash dot dot + // Pango: pango_attr_underline_new() + // none, single, double, low single, or wavy/error + // DirectWrite: only ever defined in version 1 + // none or single only + // we could do this with a custom renderer (see Petzold's articles; he does it there) + + // TODO kCTUnderlineColorAttributeName + // OS X: RGBA + // Pango: RGB(? TODO check for A in newer versions) + // DirectWrite: none; unsure if this means "same as text color" or "black only" (I assume Direct2D decides) + // we could do this with a custom renderer (see Petzold's articles; he does it there) + + // TODO kCTSuperscriptAttributeName vs below + + // TODO compare kCTVerticalFormsAttributeName to below? + + // TODO kCTGlyphInfoAttributeName + + // TODO kCTCharacterShapeAttributeName (seems to be provided by pango as well) + + // TODO kCTLanguageAttributeName? + + // TODO kCTRunDelegateAttributeName? + + // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName + + // TODO kCTWritingDirectionAttributeName? + + // TODO kCTRubyAnnotationAttributeName vs below #if 0 From aa7d1e28ead53fd2209ec670375343a7bfca487a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 02:55:42 -0500 Subject: [PATCH 153/487] More TODOs. --- ui_attrstr.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 4c967254..dda453ce 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -62,6 +62,9 @@ _UI_ENUM(uiAttribute) { // TODO kCTRubyAnnotationAttributeName vs below + // TODO strikethroughs? + // TODO see what Pango and DirectWrite provide that Core Text doesn't seem to + #if 0 // These attributes represent typographic features. Each feature From 250cba7c6733de6b03c0e7a35304fc06c0770da2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 13:21:47 -0500 Subject: [PATCH 154/487] Added more potential attributes. --- ui_attrstr.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index dda453ce..666113e1 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -62,8 +62,10 @@ _UI_ENUM(uiAttribute) { // TODO kCTRubyAnnotationAttributeName vs below - // TODO strikethroughs? - // TODO see what Pango and DirectWrite provide that Core Text doesn't seem to + // TODO strikethroughs? (pango yes, directwrite yes, os x no) + // TODO baseline offsets? (pango yes) + // TODO size scales? (pango yes) + // TODO fallbacks (pango: enable or disable) #if 0 From eb7dfa5cc4a325f4c39251e5b85f6c6c9c657dcb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 14:21:58 -0500 Subject: [PATCH 155/487] More attribute work, including trimming unsupported or nonsensical attributes. --- ui_attrstr.h | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 666113e1..8152c8d5 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -28,38 +28,13 @@ _UI_ENUM(uiAttribute) { // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? - // TODO underline styles - // OS X: kCTUnderlineStyleAttributeName - // none, single, thick, or double - // styles: solid, dot, dash, dash dot, dash dot dot - // Pango: pango_attr_underline_new() - // none, single, double, low single, or wavy/error - // DirectWrite: only ever defined in version 1 - // none or single only - // we could do this with a custom renderer (see Petzold's articles; he does it there) - - // TODO kCTUnderlineColorAttributeName - // OS X: RGBA - // Pango: RGB(? TODO check for A in newer versions) - // DirectWrite: none; unsure if this means "same as text color" or "black only" (I assume Direct2D decides) - // we could do this with a custom renderer (see Petzold's articles; he does it there) + uiAttributeUnderline, // enum uiDrawUnderlineStyle + uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO kCTSuperscriptAttributeName vs below - // TODO compare kCTVerticalFormsAttributeName to below? - - // TODO kCTGlyphInfoAttributeName - - // TODO kCTCharacterShapeAttributeName (seems to be provided by pango as well) - - // TODO kCTLanguageAttributeName? - - // TODO kCTRunDelegateAttributeName? - // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName - // TODO kCTWritingDirectionAttributeName? - // TODO kCTRubyAnnotationAttributeName vs below // TODO strikethroughs? (pango yes, directwrite yes, os x no) @@ -215,6 +190,20 @@ _UI_ENUM(uiAttribute) { // TODO uiAttributeCustom, }; +_UI_ENUM(uiDrawUnderlineStyle) { + uiDrawUnderlineStyleNone, + uiDrawUnderlineStyleSingle, + uiDrawUnderlineStyleDouble, + uiDrawUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +_UI_ENUM(uiDrawUnderlineColor) { + uiDrawUnderlineColorCustom, // also use R/G/B/A fields + uiDrawUnderlineColorSpelling, + uiDrawUnderlineColorGrammar, + uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X +}; + _UI_ENUM(uiAttributeFractionForm) { uiAttributeFractionFormNone, uiAttributeFractionFormVertical, @@ -402,6 +391,7 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); +// TODO vertical carets _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking From 4195bc3b4b16620cc95d64f1b3e3689dc3a9ed3d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 14:57:56 -0500 Subject: [PATCH 156/487] Finalized and implemented underlines on OS X. --- common/attrlist.c | 6 ++ darwin/attrstr.m | 126 +++++++++++++++++++++++++++++---- darwin/main.m | 3 + darwin/uipriv_darwin.h | 2 + examples/drawtext/attributes.c | 39 +++++++++- ui_attrstr.h | 1 + 6 files changed, 164 insertions(+), 13 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 1ca52537..746b1e76 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -302,6 +302,12 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) case uiAttributeSize: // TODO use a closest match? return attr->spec.Double == spec->Double; + case uiAttributeUnderlineColor: + if (attr->spec.Value != spec->Value) + return 0; + if (attr->spec.Value != uiDrawUnderlineColorCustom) + return 1; + // otherwise fall through case uiAttributeColor: case uiAttributeBackground: // TODO use a closest match? diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 051bec16..0a5dd8ac 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,6 +1,59 @@ // 12 february 2017 #import "uipriv_darwin.h" +// this is what AppKit does internally +// WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm +static NSColor *spellingColor = nil; +static NSColor *grammarColor = nil; +static NSColor *auxiliaryColor = nil; + +static NSColor *tryColorNamed(NSString *name) +{ + NSImage *img; + + img = [NSImage imageNamed:name]; + if (img == nil) + return nil; + return [NSColor colorWithPatternImage:img]; +} + +void initUnderlineColors(void) +{ + spellingColor = tryColorNamed(@"NSSpellingDot"); + if (spellingColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + spellingColor = tryColorNamed(@"SpellingDot"); + if (spellingColor == nil) + spellingColor = [NSColor redColor]; + } + [spellingColor retain]; // override autoreleasing + + grammarColor = tryColorNamed(@"NSGrammarDot"); + if (grammarColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + grammarColor = tryColorNamed(@"GrammarDot"); + if (grammarColor == nil) + grammarColor = [NSColor greenColor]; + } + [grammarColor retain]; // override autoreleasing + + auxiliaryColor = tryColorNamed(@"NSCorrectionDot"); + if (auxiliaryColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + auxiliaryColor = tryColorNamed(@"CorrectionDot"); + if (auxiliaryColor == nil) + auxiliaryColor = [NSColor blueColor]; + } + [auxiliaryColor retain]; // override autoreleasing +} + +void uninitUnderlineColors(void) +{ + [auxiliaryColor release]; + [grammarColor release]; + [spellingColor release]; +} + // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute // TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? @@ -64,15 +117,34 @@ static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, dou }); } +static CGColorRef mkcolor(uiAttributeSpec *spec) +{ + CGColorSpaceRef colorspace; + CGColorRef color; + CGFloat components[4]; + + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (colorspace == NULL) { + // TODO + } + components[0] = spec->R; + components[1] = spec->G; + components[2] = spec->B; + components[3] = spec->A; + color = CGColorCreate(colorspace, components); + CFRelease(colorspace); + return color; +} + static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; - CGColorSpaceRef colorspace; CGColorRef color; - CGFloat components[4]; size_t ostart, oend; backgroundBlock block; + int32_t us; + CFNumberRef num; ostart = start; oend = end; @@ -112,16 +184,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t }); break; case uiAttributeColor: - colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - if (colorspace == NULL) { - // TODO - } - components[0] = spec->R; - components[1] = spec->G; - components[2] = spec->B; - components[3] = spec->A; - color = CGColorCreate(colorspace, components); - CFRelease(colorspace); + color = mkcolor(spec); CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); CFRelease(color); break; @@ -137,6 +200,45 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t else CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); break; + case uiAttributeUnderline: + switch (spec->Value) { + case uiDrawUnderlineStyleNone: + us = kCTUnderlineStyleNone; + break; + case uiDrawUnderlineStyleSingle: + us = kCTUnderlineStyleSingle; + break; + case uiDrawUnderlineStyleDouble: + us = kCTUnderlineStyleDouble; + break; + case uiDrawUnderlineStyleSuggestion: + // TODO incorrect if a solid color + us = kCTUnderlineStyleThick; + break; + } + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &us); + CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineStyleAttributeName, num); + CFRelease(num); + break; + case uiAttributeUnderlineColor: + switch (spec->Value) { + case uiDrawUnderlineColorCustom: + color = mkcolor(spec); + break; + case uiDrawUnderlineColorSpelling: + color = [spellingColor CGColor]; + break; + case uiDrawUnderlineColorGrammar: + color = [grammarColor CGColor]; + break; + case uiDrawUnderlineColorAuxiliary: + color = [auxiliaryColor CGColor]; + break; + } + CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineColorAttributeName, color); + if (spec->Value == uiDrawUnderlineColorCustom) + CFRelease(color); + break; // TODO } return 0; diff --git a/darwin/main.m b/darwin/main.m index 59a8683b..440343bc 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -125,6 +125,8 @@ const char *uiInit(uiInitOptions *o) [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; setupFontPanel(); + + initUnderlineColors(); } globalPool = [[NSAutoreleasePool alloc] init]; @@ -140,6 +142,7 @@ void uiUninit(void) [globalPool release]; @autoreleasepool { + uninitUnderlineColors(); [delegate release]; [realNSApp() setDelegate:nil]; [app release]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index bf009522..32ae8688 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,5 +145,7 @@ extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m +extern void initUnderlineColors(void); +extern void uninitUnderlineColors(void); typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 1aa74a72..6e54dfcb 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -98,8 +98,45 @@ static void setupAttributedString(void) spec.Type = uiAttributeVerticalForms; spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSingle; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " "); + next = "underlines"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleDouble; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeUnderlineColor; + spec.Value = uiDrawUnderlineColorCustom; + spec.R = 0.5; + spec.G = 0.0; + spec.B = 1.0; + spec.A = 1.0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " ("); + next = "including underlines for spelling correction and the like"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSuggestion; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeUnderlineColor; + spec.Value = uiDrawUnderlineColorSpelling; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + uiAttributedStringAppendUnattributed(attrstr, ", "); next = "multiple TODO"; diff --git a/ui_attrstr.h b/ui_attrstr.h index 8152c8d5..c3738a42 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -29,6 +29,7 @@ _UI_ENUM(uiAttribute) { // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? uiAttributeUnderline, // enum uiDrawUnderlineStyle + // TODO what is the color in the case we don't specify it, black or the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO kCTSuperscriptAttributeName vs below From e4b761d611c5bc2e1a999a52a7dc9bdd7570c5b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 17:15:19 -0500 Subject: [PATCH 157/487] Defined more of the OpenType attributes. --- ui_attrstr.h | 113 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 42 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index c3738a42..a0b8a2d6 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -26,13 +26,12 @@ _UI_ENUM(uiAttribute) { // TODO kCTLigatureAttributeName vs below - // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? - uiAttributeUnderline, // enum uiDrawUnderlineStyle // TODO what is the color in the case we don't specify it, black or the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO kCTSuperscriptAttributeName vs below + // all it does is set the below attribute so // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName @@ -65,58 +64,59 @@ _UI_ENUM(uiAttribute) { uiAttributeCommonLigatures, // 0 = off, 1 = on uiAttributeRequiredLigatures, // 0 = off, 1 = on - uiAttributeRareLigatures, // 0 = off, 1 = on - uiAttributeLogoLigatures, // 0 = off, 1 = on - uiAttributeRebusPictureLigatures, // 0 = off, 1 = on - uiAttributeDipthrongLigatures, // 0 = off, 1 = on - uiAttributeSquaredLigatures, // 0 = off, 1 = on - // TODO rename - uiAttributeAbbreviatedSquared, // 0 = off, 1 = on - uiAttributeSymbolLigatures, // 0 = off, 1 = on + // AAT calls these "rare ligatures" + uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on uiAttributeContextualLigatures, // 0 = off, 1 = on uiAttributeHistoricalLigatures, // 0 = off, 1 = on - uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all + // TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all - uiAttributeLetterCasing, // TODO - // kLetterCaseType + uiAttributeUnicase, // 0 = off, 1 = on - uiAttributeLinguisticRearrangement, // 0 = off, 1 = on + // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on - uiAttributeNumberSpacing, // TODO - // kNumberSpacingType + // AAT: if off, "monospaced number spacing"; if on, "proportional number spacing" + // OpenType: non-proportional numbers are called "tabular" + // TODO uiAttributeProportionalNumbers, // 0 = off, 1 = on + // TODO kNumberSpacingType 0 or 1 + // TODO really? // TODO kSmartSwashType // TODO kDiacriticsType - // TODO kVerticalPositionType + uiAttributeSuperscripts, // enum uiAttributeSuperscript uiAttributeFractionForms, // enum uiAttributeFractionForm - // TODO kOverlappingCharactersType - - // TODO kTypographicExtrasType uiAttributeSlashedZero, // 0 = off, 1 = on - // TODO kMathematicalExtrasType uiAttributeMathematicalGreek, // 0 = off, 1 = on - // TODO kOrnamentSetsType + // AAT defines the following values: + // 0 = none + // 1 = dingbats + // 2 = pi characters + // 3 = fleurons + // 4 = decorative borders + // 5 = international symbols + // 6 = mathematical symbols + // OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many + uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? // TODO kCharacterAlternativesType - // TODO kDesignComplexityType + uiAttributeTitlingCapitalForms, // 0 = off, 1 = on - // TODO kStyleOptionsType - // TODO titling? + // AAT calls these "character shapes" + uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm - // TODO kCharacterShapeType - // we could probably make the enum now - - uiAttributeNumberCase, // TODO + // OpenType calls these "old-style" + uiAttributeLowercaseNumbers, // 0 = off, 1 = on // TODO kTextSpacingType + // see kKanaSpacingType below // TODO kTransliterationType @@ -133,26 +133,27 @@ _UI_ENUM(uiAttribute) { // 9 = inverted box // 10 = inverted rounded box uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? // TODO kKanaSpacingType - // TODO kIdeographicSpacingType + // should these be provided? CAN they be provided? // TODO kUnicodeDecompositionType // TODO kRubyKanaType - // TODO kCJKSymbolAlternativesType - - // TODO kIdeographicAlternativesType - // TODO kCJKVerticalRomanPlacementType + // this is 'valt' in OpenType but I don't know if I want to make it selectable or not - // TODO kItalicCJKRomanType + uiAttributeCJKRomansToItalics, // 0 = off, 1 = on - // TODO kCaseSensitiveLayoutType + uiAttributeCaseSensitiveLayout, // 0 = off, 1 = on + // AAT: this is called "case-sensitive spacing" + uiAttributeCapitalSpacing, // 0 = off, 1 = on - // TODO kAlternateKanaType + uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on + uiAttributeAlternateVerticalKana, // 0 = off, 1 = on uiAttributeStylisticAlternative1, // 0 = off, 1 = on uiAttributeStylisticAlternative2, // 0 = off, 1 = on @@ -175,11 +176,12 @@ _UI_ENUM(uiAttribute) { uiAttributeStylisticAlternative19, // 0 = off, 1 = on uiAttributeStylisticAlternative20, // 0 = off, 1 = on - // TODO kContextualAlternatesType + uiAttributeContextualAlternates, // 0 = off, 1 = on + uiAttributeSwashes, // 0 = off, 1 = on + uiAttributeContextualSwashes, // 0 = off, 1 = on - // TODO kLowerCaseType - - // TODO kUpperCaseType + uiAttributeLowercaseCapForms, // enum uiAttributeCapForm + uiAttributeUppercaseCapForms, // enum uiAttributeCapForm // TODO kLanguageTagType? @@ -187,7 +189,7 @@ _UI_ENUM(uiAttribute) { #endif - // TODO uiAttributeSystem, + // TODO uiAttributeSystem, (this might not be doable with DirectWrite) // TODO uiAttributeCustom, }; @@ -205,12 +207,39 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +_UI_ENUM(uiAttributeSuperscript) { + uiAttributeSuperscriptNone, + uiAttributeSuperscriptSuperscript, // AAT: "superior" + uiAttributeSuperscriptSubscript, // AAT: "inferior" + uiAttributeSuperscriptOrdinal, + uiAttributeSuperscriptScientificInferior, +}; + _UI_ENUM(uiAttributeFractionForm) { uiAttributeFractionFormNone, uiAttributeFractionFormVertical, uiAttributeFractionFormDiagonal, }; +_UI_ENUM(uiAttributeHanCharacterForm) { + uiAttributeHanCharacterFormTraditional, + uiAttributeHanCharacterFormSimplified, + uiAttributeHanCharacterFormJIS1978, + uiAttributeHanCharacterFormJIS1983, + uiAttributeHanCharacterFormJIS1990, + uiAttributeHanCharacterFormExpert, + uiAttributeHanCharacterFormJIS2004, + uiAttributeHanCharacterFormHojo, + uiAttributeHanCharacterFormNLC, + uiAttributeHanCharacterFormTraditionalNames, +}; + +_UI_ENUM(uiAttributeCapForm) { + uiAttributeCapFormNormal, + uiAttributeCapFormSmallCaps, + uiAttributeCapFormPetiteCaps, +}; + // TODO number case struct uiAttributeSpec { From 2ffcd192c063871c82002d72c70f2dfb7d368352 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 19:00:35 -0500 Subject: [PATCH 158/487] Wrote the common code (for DirectWrite and Pango) to deal with OpenType features. Now to merge with Core Text's internal AAT-to-OpenType mapping. --- common/opentype.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 8 +- 2 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 common/opentype.c diff --git a/common/opentype.c b/common/opentype.c new file mode 100644 index 00000000..12d14699 --- /dev/null +++ b/common/opentype.c @@ -0,0 +1,227 @@ +// 14 february 2017 +#include "../ui.h" +#include "uipriv.h" + +// note: each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover + +typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); + +static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) +{ + if (spec->Value != 0) { + (*f)(featureTag, 1, data); + return; + } + (*f)(featureTag, 0, data); +} + +void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) +{ + switch (spec->Type) { + case uiAttributeStandardLigatures: + boolspec(spec, "liga", f, data); + return; + case uiAttributeRequiredLigatures: + boolspec(spec, "rlig", f, data); + return; + case uiAttributeDiscretionaryLigatures: + boolspec(spec, "dlig", f, data); + return; + case uiAttributeContextualLigatures: + boolspec(spec, "clig", f, data); + return; + case uiAttributeHistoricalLigatures: + boolspec(spec, "hlig", f, data); + return; + case uiAttributeUnicase: + boolspec(spec, "unic", f, data); + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeSuperscripts: + switch (spec->Value) { + case uiAttributeSuperscriptSuperscript: + (*f)("sups", 1, data); + break; + case uiAttributeSuperscriptSubscript: + (*f)("subs", 1, data); + break; + case uiAttributeSuperscriptOrdinal: + (*f)("ordn", 1, data); + break; + case uiAttributeSuperscriptScientificInferior: + (*f)("sinf", 1, data); + break; + } + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeFractionForms: + switch (spec->Value) { + case uiAttributeFractionFormVertical: + (*f)("afrc", 1, data); + break; + case uiAttributeFractionFormDiagonal: + (*f)("frac", 1, data); + break; + } + return; + case uiAttributeSlashedZero: + boolspec(spec, "zero", data); + return; + case uiAttributeMathematicalGreek: + boolspec(spec, "mgrk", data); + return; + case uiAttributeOrnamentalForms: + (*f)("ornm", (uint32_t) (spec->Value), data); + return; + case uiAttributeTitlingCapitalForms: + boolspec(spec, "titl", data); + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeHanCharacterForms: + switch (spec->Value) { + case uiAttributeHanCharacterFormTraditional: + (*f)("trad", 1, data); + break; + case uiAttributeHanCharacterFormSimplified: + (*f)("smpl", 1, data); + break; + case uiAttributeHanCharacterFormJIS1978: + (*f)("jp78", 1, data); + break; + case uiAttributeHanCharacterFormJIS1983: + (*f)("jp83", 1, data); + break; + case uiAttributeHanCharacterFormJIS1990: + (*f)("jp90", 1, data); + break; + case uiAttributeHanCharacterFormExpert: + (*f)("expt", 1, data); + break; + case uiAttributeHanCharacterFormJIS2004: + (*f)("jp04", 1, data); + break; + case uiAttributeHanCharacterFormHojo: + (*f)("hojo", 1, data); + break; + case uiAttributeHanCharacterFormNLC: + (*f)("nlck", 1, data); + break; + case uiAttributeHanCharacterFormTraditionalNames: + (*f)("tnam", 1, data); + break; + } + return; + case uiAttributeLowercaseNumbers: + boolspec(spec, "onum", data); + return; + case uiAttributeGlyphAnnotations: + (*f)("nalt", (uint32_t) (spec->Value), data); + return; + case uiAttributeCJKRomanToitalics: + boolspec(spec, "ital", data); + return; + case uiAttributeCaseSensitiveForms: + boolspec(spec, "case", data); + return; + case uiAttributeCapitalSpacing: + boolspec(spec, "cpsp", data); + return; + case uiAttributeAlternateHorizontalKana: + boolspec(spec, "hkna", data); + return; + case uiAttributeAlternateVerticalKana: + boolspec(spec, "vkna", data); + return; + case uiAttributeStylisticAlternative1: + boolspec(spec, "ss01", data); + return; + case uiAttributeStylisticAlternative2: + boolspec(spec, "ss02", data); + return; + case uiAttributeStylisticAlternative3: + boolspec(spec, "ss03", data); + return; + case uiAttributeStylisticAlternative4: + boolspec(spec, "ss04", data); + return; + case uiAttributeStylisticAlternative5: + boolspec(spec, "ss05", data); + return; + case uiAttributeStylisticAlternative6: + boolspec(spec, "ss06", data); + return; + case uiAttributeStylisticAlternative7: + boolspec(spec, "ss07", data); + return; + case uiAttributeStylisticAlternative8: + boolspec(spec, "ss08", data); + return; + case uiAttributeStylisticAlternative9: + boolspec(spec, "ss09", data); + return; + case uiAttributeStylisticAlternative10: + boolspec(spec, "ss10", data); + return; + case uiAttributeStylisticAlternative11: + boolspec(spec, "ss11", data); + return; + case uiAttributeStylisticAlternative12: + boolspec(spec, "ss12", data); + return; + case uiAttributeStylisticAlternative13: + boolspec(spec, "ss13", data); + return; + case uiAttributeStylisticAlternative14: + boolspec(spec, "ss14", data); + return; + case uiAttributeStylisticAlternative15: + boolspec(spec, "ss15", data); + return; + case uiAttributeStylisticAlternative16: + boolspec(spec, "ss16", data); + return; + case uiAttributeStylisticAlternative17: + boolspec(spec, "ss17", data); + return; + case uiAttributeStylisticAlternative18: + boolspec(spec, "ss18", data); + return; + case uiAttributeStylisticAlternative19: + boolspec(spec, "ss19", data); + return; + case uiAttributeStylisticAlternative20: + boolspec(spec, "ss20", data); + return; + case uiAttributeContextualAlternates: + boolspec(spec, "calt", data); + return; + case uiAttributeSwashes: + boolspec(spec, "swsh", data); + return; + case uiAttributeContextualSwashes: + boolspec(spec, "cswh", data); + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeLowercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormSmallCaps: + (*f)("smcp", 1, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)("pcap", 1, data); + break; + } + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeUppercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormSmallCaps: + (*f)("c2sc", 1, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)("c2pc", 1, data); + break; + } + return; + } +} diff --git a/ui_attrstr.h b/ui_attrstr.h index a0b8a2d6..3da40fe1 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -62,7 +62,8 @@ _UI_ENUM(uiAttribute) { // TODO kAllTypographicFeaturesType - uiAttributeCommonLigatures, // 0 = off, 1 = on + // AAT calls these "common ligatures" + uiAttributeStandardLigatures, // 0 = off, 1 = on uiAttributeRequiredLigatures, // 0 = off, 1 = on // AAT calls these "rare ligatures" uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on @@ -132,6 +133,7 @@ _UI_ENUM(uiAttribute) { // 8 = diamond // 9 = inverted box // 10 = inverted rounded box + // TODO rename to AnnotatedForms? uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? @@ -148,13 +150,15 @@ _UI_ENUM(uiAttribute) { uiAttributeCJKRomansToItalics, // 0 = off, 1 = on - uiAttributeCaseSensitiveLayout, // 0 = off, 1 = on + // AAT calls this "case-sensitive layout" + uiAttributeCaseSensitiveForms, // 0 = off, 1 = on // AAT: this is called "case-sensitive spacing" uiAttributeCapitalSpacing, // 0 = off, 1 = on uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on uiAttributeAlternateVerticalKana, // 0 = off, 1 = on + // TODO "Alternate"? unify all this uiAttributeStylisticAlternative1, // 0 = off, 1 = on uiAttributeStylisticAlternative2, // 0 = off, 1 = on uiAttributeStylisticAlternative3, // 0 = off, 1 = on From 16b0ca518e57cbd8bd1def4f65e068768deaee75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 21:18:56 -0500 Subject: [PATCH 159/487] Synced opentype.c to AAT and more TODOs. --- common/opentype.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++- ui_attrstr.h | 25 +++++++++++++------- 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index 12d14699..f561eeab 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -2,7 +2,8 @@ #include "../ui.h" #include "uipriv.h" -// note: each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover +// Notes: +// - Each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); @@ -15,6 +16,15 @@ static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTy (*f)(featureTag, 0, data); } +static void boolspecnot(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) +{ + if (spec->Value == 0) { + (*f)(featureTag, 1, data); + return; + } + (*f)(featureTag, 0, data); +} + void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) { switch (spec->Type) { @@ -32,11 +42,25 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) return; case uiAttributeHistoricalLigatures: boolspec(spec, "hlig", f, data); + // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too + boolspec(spec, "hist", f, data); return; case uiAttributeUnicase: boolspec(spec, "unic", f, data); return; // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeNumberSpacings: + // TODO does Core Text set both? do we? + switch (spec->Value) { + case uiAttributeNumberSpacingProportional: + (*f)("pnum", 1, data); + break; + case uiAttributeNumberSpacingTitling: + (*f)("tnum", 1, data); + break; + } + return; + // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeSuperscripts: switch (spec->Value) { case uiAttributeSuperscriptSuperscript: @@ -73,6 +97,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeOrnamentalForms: (*f)("ornm", (uint32_t) (spec->Value), data); return; + case uiAttributeSpecificCharacterForm: + (*f)("aalt", (uint32_t) (spec->Value), data); + return; case uiAttributeTitlingCapitalForms: boolspec(spec, "titl", data); return; @@ -113,6 +140,12 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) return; case uiAttributeLowercaseNumbers: boolspec(spec, "onum", data); + // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too + // TODO is it always set? + boolspecnot(spec, "lnum", data); + return; + case uiAttributeHanjaToHangul: + boolspec(spec, "hngl", data); return; case uiAttributeGlyphAnnotations: (*f)("nalt", (uint32_t) (spec->Value), data); @@ -225,3 +258,26 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) return; } } + +// TODO missing that AAT uses directly: +// - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) +// - ruby +// missing that AAT knows about: +// - abvf, abvm, abvs, blwf, blwm, blws (baselines) +// - akhn, dist, half, haln, nukt, rkrf, rphf, vatu (Devanagari support) +// - ccmp (compositions) +// - cjct, pres, pstf, psts (Indic script support) +// - curs (cursive positioning) +// - dnom, numr (fraction parts) +// - falt, fina, init, isol, jalt, medi, mset (Arabic support) +// - rclt (required contextual alternates) +// - fin2, fin3, med2 (Syriac script support) +// - lfbd, opbd, rtbd (optical bounds support) +// - ljmo, tjmo, vjmo (Hangul Jamo support) +// - locl (Cyrillic support) +// - ltra, ltrm, rtla, rtlm (bidi support) +// - mark, mkmk (mark positioning) +// - pref (Khmer support) +// - rand (random glyph selection candidates) +// - salt (stylistic alternatives) +// - size (sizing info) diff --git a/ui_attrstr.h b/ui_attrstr.h index 3da40fe1..c33e3fb6 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -76,11 +76,8 @@ _UI_ENUM(uiAttribute) { // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on - // AAT: if off, "monospaced number spacing"; if on, "proportional number spacing" - // OpenType: non-proportional numbers are called "tabular" - // TODO uiAttributeProportionalNumbers, // 0 = off, 1 = on - // TODO kNumberSpacingType 0 or 1 - // TODO really? + // TODO rename this + uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing // TODO kSmartSwashType @@ -106,7 +103,13 @@ _UI_ENUM(uiAttribute) { uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? - // TODO kCharacterAlternativesType + // AAT calls this "character alternatives" and defines the + // following values: + // 0 = none + // OpenType calls this "access all alternates". + // TODO doesn't OpenType do the same about 0? + uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? uiAttributeTitlingCapitalForms, // 0 = off, 1 = on @@ -119,7 +122,7 @@ _UI_ENUM(uiAttribute) { // TODO kTextSpacingType // see kKanaSpacingType below - // TODO kTransliterationType + uiAttributeHanjaToHangul, // 0 = off, 1 = on // AAT defines the following values: // 0 = none @@ -139,7 +142,7 @@ _UI_ENUM(uiAttribute) { // TODO kKanaSpacingType // TODO kIdeographicSpacingType - // should these be provided? CAN they be provided? + // can they be provided independently of kTextSpacingType? Core Text doesn't seem to // TODO kUnicodeDecompositionType @@ -211,6 +214,12 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +_UI_ENUM(uiAttributeNumberSpacing) { + uiAttributeNumberSpacingProportional, + // AAT calls this "monospaced" + uiAttributeNumberSpacingTabular, +}; + _UI_ENUM(uiAttributeSuperscript) { uiAttributeSuperscriptNone, uiAttributeSuperscriptSuperscript, // AAT: "superior" From da22adac7ffefac4737dd7865eb9994d502ca5d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 22:37:01 -0500 Subject: [PATCH 160/487] More feature list completeness work. --- common/opentype.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index f561eeab..56c2f572 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -3,7 +3,7 @@ #include "uipriv.h" // Notes: -// - Each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover +// - Each tag should only appear in quotes once (including within comments); this allows automated tools to determine what we cover and don't cover typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); @@ -264,20 +264,48 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // - ruby // missing that AAT knows about: // - abvf, abvm, abvs, blwf, blwm, blws (baselines) -// - akhn, dist, half, haln, nukt, rkrf, rphf, vatu (Devanagari support) +// - harfbuzz says tibetan uses this // - ccmp (compositions) -// - cjct, pres, pstf, psts (Indic script support) // - curs (cursive positioning) // - dnom, numr (fraction parts) -// - falt, fina, init, isol, jalt, medi, mset (Arabic support) +// - falt, jalt (Arabic support) // - rclt (required contextual alternates) -// - fin2, fin3, med2 (Syriac script support) // - lfbd, opbd, rtbd (optical bounds support) -// - ljmo, tjmo, vjmo (Hangul Jamo support) // - locl (Cyrillic support) // - ltra, ltrm, rtla, rtlm (bidi support) // - mark, mkmk (mark positioning) -// - pref (Khmer support) // - rand (random glyph selection candidates) // - salt (stylistic alternatives) // - size (sizing info) +// +// script-specific; core text and pango/harfbuzz use these automatically based on the language +// TODO if DirectWrite does too we can ignore them and just provide a language attribute (they all use BCP 47 syntax for language names) +// Tag Core Text? Harfbuzz? +// akhn TODO yes +// cjct TODO yes +// dist TODO yes +// falt TODO TODO +// fin2 TODO yes +// fin3 TODO yes +// fina TODO yes +// half TODO yes +// haln TODO yes +// init TODO yes +// isol TODO yes +// jalt TODO TODO +// ljmo TODO yes +// locl TODO all horz(!) +// med2 TODO yes +// medi TODO yes +// mset TODO yes +// nukt TODO yes +// pref TODO yes +// pres TODO yes +// pstf TODO yes +// psts TODO yes +// rclt TODO all horz(!) +// rkrf TODO yes +// rphf TODO yes +// tjmo TODO yes +// vatu TODO yes +// vjmo TODO yes From bda35b40bdd45dcdb4a3d1c7607e5451ff78a63e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 23:56:20 -0500 Subject: [PATCH 161/487] Filled in Core Text language details. --- common/opentype.c | 55 ++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index 56c2f572..282df9ba 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -263,10 +263,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) // - ruby // missing that AAT knows about: -// - abvf, abvm, abvs, blwf, blwm, blws (baselines) -// - harfbuzz says tibetan uses this // - ccmp (compositions) // - curs (cursive positioning) +// - Core Text uses this in language-specific stuff // - dnom, numr (fraction parts) // - falt, jalt (Arabic support) // - rclt (required contextual alternates) @@ -281,31 +280,37 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // script-specific; core text and pango/harfbuzz use these automatically based on the language // TODO if DirectWrite does too we can ignore them and just provide a language attribute (they all use BCP 47 syntax for language names) // Tag Core Text? Harfbuzz? -// akhn TODO yes -// cjct TODO yes -// dist TODO yes +// abvf yes yes +// abvm yes yes +// abvs yes TODO +// akhn yes yes +// blwf yes yes +// blwm yes yes +// blws yes TODO +// cjct yes yes +// dist yes yes // falt TODO TODO -// fin2 TODO yes -// fin3 TODO yes -// fina TODO yes -// half TODO yes -// haln TODO yes -// init TODO yes -// isol TODO yes +// fin2 yes yes +// fin3 yes yes +// fina yes yes +// half yes yes +// haln yes yes +// init yes yes +// isol yes yes // jalt TODO TODO -// ljmo TODO yes +// ljmo yes yes // locl TODO all horz(!) -// med2 TODO yes -// medi TODO yes +// med2 yes yes +// medi yes yes // mset TODO yes -// nukt TODO yes -// pref TODO yes -// pres TODO yes -// pstf TODO yes -// psts TODO yes +// nukt yes yes +// pref yes yes +// pres yes yes +// pstf yes yes +// psts yes yes // rclt TODO all horz(!) -// rkrf TODO yes -// rphf TODO yes -// tjmo TODO yes -// vatu TODO yes -// vjmo TODO yes +// rkrf yes yes +// rphf yes yes +// tjmo yes yes +// vatu yes yes +// vjmo yes yes From 81b520249b213fffcbd3c785da6d8beb7e4eee80 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 01:34:26 -0500 Subject: [PATCH 162/487] More attribute work. Getting a clearer idea. --- TODO.md | 43 +++++++++++++++++++++++++++++++++++++++++++ common/opentype.c | 10 +++++----- ui_attrstr.h | 13 +++++-------- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index 33ac95af..8bf48004 100644 --- a/TODO.md +++ b/TODO.md @@ -127,3 +127,46 @@ label shortcut keys [02:15:00] 1.40.3 [02:20:46] I'll ahve to keep this in mind then, thanks [02:20:59] if only there was a cairo-specific attribute for alpha... + +FONT LOADING + +[00:10:08] andlabs: is there API yet to load from memory? last i checked i only found from file (which we use in builder). https://git.gnome.org/browse/gnome-builder/tree/libide/editor/ide-editor-map-bin.c#n115 +[00:13:12] mrmcq2u_ (mrmcq2u@109.79.53.90) joined the channel +[00:14:59] mrmcq2u (mrmcq2u@109.79.73.102) left IRC (Ping timeout: 181 seconds) +[00:15:19] hergertme: no, which is why I was asking =P +[00:15:30] I would have dug down if I could ensure at least something about the backends a GTK+ 3 program uses +[00:15:39] on all platforms except windows and os x +[00:16:11] to the best of my (partially outdated, given pace of foss) knowledge there isn't an api to load from memory +[00:16:28] you can possibly make a tmpdir and put a temp file in there +[00:16:52] and load that as your font dir in your FcConfig, so any PangoFontDescription would point to that one font, no matter what +[00:17:18] (using the API layed out in that link) +[00:18:18] dsr1014__ (dsr1014@c-73-72-102-18.hsd1.il.comcast.net) joined the channel +[00:35:18] simukis_ (simukis@78-60-58-6.static.zebra.lt) left IRC (Quit: simukis_) +[00:35:48] dreamon_ (dreamon@ppp-188-174-49-41.dynamic.mnet-online.de) joined the channel +[00:40:09] samtoday_ (samtoday@114-198-116-132.dyn.iinet.net.au) joined the channel +[00:40:32] mjog (mjog@120.18.225.46) joined the channel +[00:40:38] hergertme: not necessarily fontconfig +[00:40:45] it can be with ft2 or xft I guess +[00:40:55] especially since I want the API NOT to make the font part of the font panel +[00:42:07] what sort of deprecated code are you trying to support? +[00:42:35] both of those are deprecated in pango fwiw +[00:43:06] on Linux im pretty sure we use FC everywhere these days +[00:44:46] (and gtk_widget_set_font_map() is how you get your custom font into a widget without affecting the global font lists, as layed out in that link) +[00:49:14] vasaikar (vasaikar@125.16.97.121) joined the channel +[00:50:14] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Client exited) +[00:50:49] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) joined the channel +[00:51:43] PioneerAxon (PioneerAxo@122.171.61.146) left IRC (Ping timeout: 180 seconds) +[00:57:47] PioneerAxon (PioneerAxo@106.201.37.181) joined the channel +[01:03:01] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Ping timeout: 181 seconds) +[01:05:49] muhannad (muhannad@95.218.26.152) left IRC (Quit: muhannad) +[01:07:51] hergertme: hm +[01:07:54] all right, thanks +[01:08:05] hergertme: fwiw right now my requirement is 3.10 +[01:10:47] ah, well you'll probably be missing the neccesary font API on gtk_widget +[01:11:04] but pango should be fine even back as far as https://developer.gnome.org/pango/1.28/PangoFcFontMap.html +[01:11:56] good +[01:12:04] because this is for custom drawing into a DrawingArea +[01:14:12] presumably just create your PangoContext as normal, but call pango_context_set_font_map() with the map you've setup. now, the load a font from a file i dont think was added to FontConfig until later though (not sure what release) +[01:15:53] FcConfigAppFontAddFile() <-- that API +[01:16:30] great, and they don't say what version the API was added in teh docs +function: ide_editor_map_bin_add() diff --git a/common/opentype.c b/common/opentype.c index 282df9ba..f874752b 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -60,7 +60,6 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) break; } return; - // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeSuperscripts: switch (spec->Value) { case uiAttributeSuperscriptSuperscript: @@ -150,6 +149,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeGlyphAnnotations: (*f)("nalt", (uint32_t) (spec->Value), data); return; + case uiAttributeRubyKanaForms: + boolspec(spec, "ruby", data); + return; case uiAttributeCJKRomanToitalics: boolspec(spec, "ital", data); return; @@ -261,12 +263,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // TODO missing that AAT uses directly: // - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) -// - ruby // missing that AAT knows about: // - ccmp (compositions) -// - curs (cursive positioning) -// - Core Text uses this in language-specific stuff -// - dnom, numr (fraction parts) +// - dnom, numr (fraction parts) — no AAT equivalent... // - falt, jalt (Arabic support) // - rclt (required contextual alternates) // - lfbd, opbd, rtbd (optical bounds support) @@ -288,6 +287,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // blwm yes yes // blws yes TODO // cjct yes yes +// curs yes yes // dist yes yes // falt TODO TODO // fin2 yes yes diff --git a/ui_attrstr.h b/ui_attrstr.h index c33e3fb6..8707a375 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -24,8 +24,6 @@ _UI_ENUM(uiAttribute) { // Windows: requires Platform Update, SetLetterSpacing() // parameter meaning unspecified - // TODO kCTLigatureAttributeName vs below - uiAttributeUnderline, // enum uiDrawUnderlineStyle // TODO what is the color in the case we don't specify it, black or the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor @@ -35,13 +33,14 @@ _UI_ENUM(uiAttribute) { // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName - // TODO kCTRubyAnnotationAttributeName vs below - // TODO strikethroughs? (pango yes, directwrite yes, os x no) // TODO baseline offsets? (pango yes) // TODO size scales? (pango yes) // TODO fallbacks (pango: enable or disable) + // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + uiAttributeLanguage, // BCP 47 string + #if 0 // These attributes represent typographic features. Each feature @@ -79,7 +78,7 @@ _UI_ENUM(uiAttribute) { // TODO rename this uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing - // TODO kSmartSwashType + // TODO kSmartSwashType, falt and jalt // TODO kDiacriticsType @@ -146,7 +145,7 @@ _UI_ENUM(uiAttribute) { // TODO kUnicodeDecompositionType - // TODO kRubyKanaType + uiAttributeRubyKanaForms, // 0 = off, 1 = on // TODO kCJKVerticalRomanPlacementType // this is 'valt' in OpenType but I don't know if I want to make it selectable or not @@ -190,8 +189,6 @@ _UI_ENUM(uiAttribute) { uiAttributeLowercaseCapForms, // enum uiAttributeCapForm uiAttributeUppercaseCapForms, // enum uiAttributeCapForm - // TODO kLanguageTagType? - // TODO kCJKRomanSpacingType #endif From 85fd3b72af943e8753e0f28ca6963f11b522adcf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 09:44:57 -0500 Subject: [PATCH 163/487] More support for uiAttributeLanguage. --- common/attrlist.c | 29 +++++++++++++++++++++++++++++ ui_attrstr.h | 1 + 2 files changed, 30 insertions(+) diff --git a/common/attrlist.c b/common/attrlist.c index 746b1e76..3bf8caff 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -291,6 +291,32 @@ static int boolsEqual(struct attr *attr, uiAttributeSpec *spec) return attr->spec.Value != 0 && spec->Value != 0; } +// BCP 47 is ASCII-only +static int asciiStringsEqualCaseFold(const char *a, const char *b) +{ + char c, d; + + for (;;) { + if (*a == *b) { + if (*a == '\0') + return 1; + a++; + b++; + continue; + } + c = *a; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + d = *b; + if (d >= 'A' && d <= 'Z') + d += 'a' - 'A'; + if (c != d) + return 0; + a++; + b++; + } +} + static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) { if (attr->spec.Type != spec->Type) @@ -298,6 +324,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) switch (attr->spec.Type) { case uiAttributeFamily: // TODO should we start copying these strings? + // TODO should this be case-insensitive? return strcmp((char *) (attr->spec.Value), (char *) (spec->Value)) == 0; case uiAttributeSize: // TODO use a closest match? @@ -317,6 +344,8 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.A == spec->A; case uiAttributeVerticalForms: return boolsEqual(attr, spec); + case uiAttributeLanguage: + return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); // TODO } // handles the rest diff --git a/ui_attrstr.h b/ui_attrstr.h index 8707a375..fac03b0f 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -39,6 +39,7 @@ _UI_ENUM(uiAttribute) { // TODO fallbacks (pango: enable or disable) // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility uiAttributeLanguage, // BCP 47 string #if 0 From 669538e9ceebbeb147b3ca315353b0639974c658 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 16:22:42 -0500 Subject: [PATCH 164/487] Implemented uiAttributeLanguage on OS X. Untested. --- darwin/attrstr.m | 27 +++++++--- darwin/fontmatch.m | 109 +++++++++++++++++++++++++++++++++++++++++ darwin/uipriv_darwin.h | 1 + 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 0a5dd8ac..b46872ce 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,6 +1,8 @@ // 12 february 2017 #import "uipriv_darwin.h" +// LONGTERM FUTURE for typographic features, on 10.10 we can use OpenType tags directly! + // this is what AppKit does internally // WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm static NSColor *spellingColor = nil; @@ -70,8 +72,9 @@ struct foreachParams { struct fontParams { uiDrawFontDescriptor desc; uint16_t featureTypes[maxFeatures]; - uint16_t featureSpecifiers[maxFeatures]; + uint16_t featureSelectors[maxFeatures]; size_t nFeatures; + const char *language; }; static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -239,18 +242,27 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; + // locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc + case uiAttributeLanguage: + // LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->language = (const char *) (spec->Value); + }); + break; // TODO } return 0; } -static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +static CTFontRef fontdescToCTFont(struct fontParams *fp) { CTFontDescriptorRef desc; CTFontRef font; - desc = fontdescToCTFontDescriptor(fd); - font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); + desc = fontdescToCTFontDescriptor(&(fp->desc)); + desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language); + font = CTFontCreateWithFontDescriptor(desc, fp->desc.Size, NULL); CFRelease(desc); // TODO correct? return font; } @@ -263,7 +275,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) CFRange range; fp = (struct fontParams *) [val pointerValue]; - font = fontdescToCTFont(&(fp->desc)); + font = fontdescToCTFont(fp); range.location = [key integerValue]; range.length = 1; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); @@ -305,6 +317,7 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray CFAttributedStringRef base; CFMutableAttributedStringRef mas; struct foreachParams fep; + struct fontParams ffp; cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); if (cfstr == NULL) { @@ -316,7 +329,9 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray if (defaultAttrs == NULL) { // TODO } - defaultCTFont = fontdescToCTFont(p->DefaultFont); + memset(&ffp, 0, sizeof (struct fontParams)); + ffp.desc = *(p->DefaultFont); + defaultCTFont = fontdescToCTFont(&ffp); CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); CFRelease(defaultCTFont); ps = mkParagraphStyle(p); diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index e1660c8b..184f1c6e 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -254,6 +254,115 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) stretchesToCTWidths[fd->Stretch]); } +// fortunately features that aren't supported are simply ignored, so we can copy them all in +// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore +// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language) +{ + CTFontDescriptorRef new; + CFMutableArrayRef outerArray; + CFDictionaryRef innerDict; + CFNumberRef numType, numSelector; + const void *keys[2], *values[2]; + size_t i; + CFArrayRef languages; + CFIndex il, nl; + CFStringRef curlang; + char d[2]; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) { + // TODO + } + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; + for (i = 0; i < n; i++) { + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (types + i)); + numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (selectors + i)); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + + // now we have to take care of the language + // TODO can we assume this is present? + if (language != NULL) { + languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); + nl = CFArrayGetCount(languages); + d[0] = language[0]; + if (d[0] >= 'A' && d[0] <= 'Z') + d[0] += 'a' - 'A'; + d[1] = language[1]; + if (d[1] >= 'A' && d[1] <= 'Z') + d[1] += 'a' - 'A'; + for (il = 0; il < nl; il++) { + char c[2]; + + curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); + // TODO check for failure + CFStringGetBytes(curlang, CFRangeMake(0, 2), + kCFStringEncodingUTF8, 0, false, + (UInt8 *) c, 2, NULL); + if (c[0] >= 'A' && c[0] <= 'Z') + c[0] += 'a' - 'A'; + if (c[1] >= 'A' && c[1] <= 'Z') + c[1] += 'a' - 'A'; + if (c[0] == d[0] && c[1] == d[1]) + break; + } + if (il != nl) { + uint16_t typ; + + typ = kLanguageTagType; + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&typ)); + numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, + &il); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + CFRelease(languages); + } + + keys[0] = kCTFontFeatureSettingsAttribute; + values[0] = outerArray; + innerDict = CFDictionaryCreate(NULL, + keys, values, 1, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(outerArray); + new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict); + CFRelease(desc); + CFRelease(innerDict); + return new; +} + // TODO deduplicate this from italicCloseness() static uiDrawTextItalic italicFromCTItalic(CTFontDescriptorRef desc, CFDictionaryRef traits) { diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 32ae8688..9d662cd5 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -142,6 +142,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m From 6761c0a9f72c3c9b6a81ae4eb094c9c4e8ccafb9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 16:40:04 -0500 Subject: [PATCH 165/487] Added the language tag stuff to the example program. More TODOs. --- examples/drawtext/attributes.c | 25 +++++++++++++++++++++++++ examples/drawtext/main.c | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 6e54dfcb..c3da5581 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -139,6 +139,31 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; + uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "ru"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " to "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "sr"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 822382d0..848def89 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -1,6 +1,11 @@ // 17 january 2017 #include "drawtext.h" +// okay everything is definitely bugged in the OS X code +// - occasional segfaults on startup +// - very rare size attributes in the attributed string example don't terminate for a while, making everything big +// - very very rare trace/bpt faults on startup + static uiWindow *mainwin; static uiBox *box; static uiCombobox *exampleList; From ddffce6d44906c1e1a3e67bfd81b6a43c15de5ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 19:42:32 -0500 Subject: [PATCH 166/487] More work. This is annoying, stupid 10.9. --- darwin/fontmatch.m | 1 + examples/drawtext/attributes.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 184f1c6e..8d5094a7 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -327,6 +327,7 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 uint16_t typ; typ = kLanguageTagType; + il++; numType = CFNumberCreate(NULL, kCFNumberSInt16Type, (const SInt16 *) (&typ)); numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c3da5581..9d0c2310 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -139,6 +139,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + // thanks to https://twitter.com/codeman38/status/831924064012886017 next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); start = uiAttributedStringLen(attrstr); @@ -181,9 +182,8 @@ static uiDrawTextLayoutParams params; #define margins 10 static uiBox *panel; -static uiCheckbox *showExtents; static uiCheckbox *showLineBounds; -static uiCheckbox *showLineGuides; +static uiFontButton *fontButton; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -266,6 +266,15 @@ static void draw(uiAreaDrawParams *p) static struct example attributesExample; +static void changeFont(uiFontButton *b, void *data) +{ + if (defaultFont.Family != fontFamily) + uiFreeText(defaultFont.Family); + // TODO rename defaultFont + uiFontButtonFont(fontButton, &defaultFont); + redraw(); +} + // TODO share? static void checkboxChecked(uiCheckbox *c, void *data) { @@ -286,6 +295,10 @@ struct example *mkAttributesExample(void) { panel = uiNewVerticalBox(); showLineBounds = newCheckbox("Show Line Bounds"); + fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, changeFont, NULL); + // TODO set the font button to the current defaultFont + uiBoxAppend(panel, uiControl(fontButton), 0); attributesExample.name = "Attributed Text"; attributesExample.panel = uiControl(panel); From 4f5328ae652ca56f2214442bfa77631b28be5f7a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 21:11:44 -0500 Subject: [PATCH 167/487] And added the typographical features for AAT. --- darwin/aat.m | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 darwin/aat.m diff --git a/darwin/aat.m b/darwin/aat.m new file mode 100644 index 00000000..05622f3c --- /dev/null +++ b/darwin/aat.m @@ -0,0 +1,389 @@ +// 14 february 2017 +#import "uipriv_darwin.h" + +typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); + +static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, specToAATEnumFunc f, void *data) +{ + if (spec->Value != 0) { + (*f)(type, ifTrue, data); + return; + } + (*f)(type, ifFalse, data); +} + +void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) +{ + switch (spec->Type) { + case uiAttributeStandardLigatures: + boolspec(spec, kLigaturesType, + kCommonLigaturesOnSelector, + kCommonLigaturesOffSelector, + f, data); + return; + case uiAttributeRequiredLigatures: + boolspec(spec, kLigaturesType, + kRequiredLigaturesOnSelector, + kRequiredLigaturesOffSelector, + f, data); + return; + case uiAttributeDiscretionaryLigatures: + boolspec(spec, kLigaturesType, + kRareLigaturesOnSelector, + kRareLigaturesOffSelector, + f, data); + return; + case uiAttributeContextualLigatures: + boolspec(spec, kLigaturesType, + kContextualLigaturesOnSelector, + kContextualLigaturesOffSelector, + f, data); + return; + case uiAttributeHistoricalLigatures: + boolspec(spec, kLigaturesType, + kHistoricalLigaturesOnSelector, + kHistoricalLigaturesOffSelector, + f, data); + return; + case uiAttributeUnicase: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table + (*f)(kLetterCaseType, 14, data); + return; + // TODO make an array? + case uiAttributeNumberSpacings: + switch (spec->Value) { + case uiAttributeNumberSpacingProportional: + (*f)(kNumberSpacingType, kProportionalNumbersSelector, data); + break; + case uiAttributeNumberSpacingTitling: + (*f)(kNumberSpacingType, kMonospacedNumbersSelector, data); + break; + } + return; + // TODO make an array? + case uiAttributeSuperscripts: + switch (spec->Value) { + case uiAttributeSuperscriptNone: + (*f)(kVerticalPositionType, kNormalPositionSelector, data); + break; + case uiAttributeSuperscriptSuperscript: + (*f)(kVerticalPositionType, kSuperiorsSelector, data); + break; + case uiAttributeSuperscriptSubscript: + (*f)(kVerticalPositionType, kInferiorsSelector, data); + break; + case uiAttributeSuperscriptOrdinal: + (*f)(kVerticalPositionType, kOrdinalsSelector, data); + break; + case uiAttributeSuperscriptScientificInferior: + (*f)(kVerticalPositionType, kScientificInferiorsSelector, data); + break; + } + return; + // TODO make an array? + case uiAttributeFractionForms: + switch (spec->Value) { + case uiAttributeFractionFormNone: + (*f)(kFractionsType, kNoFractionsSelector, data); + break; + case uiAttributeFractionFormVertical: + (*f)(kFractionsType, kVerticalFractionsSelector, data); + break; + case uiAttributeFractionFormDiagonal: + (*f)(kFractionsType, kDiagonalFractionsSelector, data); + break; + } + return; + case uiAttributeSlashedZero: + boolspec(spec, kTypographicExtrasType, + kSlashedZeroOnSelector, + kSlashedZeroOffSelector, + f, data); + return; + case uiAttributeMathematicalGreek: + boolspec(spec, kMathematicalExtrasType, + kMathematicalGreekOnSelector, + kMathematicalGreekOffSelector, + f, data); + return; + case uiAttributeOrnamentalForms: + (*f)(kOrnamentSetsType, (uint16_t) (spec->Value), data); + return; + case uiAttributeSpecificCharacterForm: + (*f)(kCharacterAlternativesType, (uint16_t) (spec->Value), data); + return; + case uiAttributeTitlingCapitalForms: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + (*f)(kStyleOptionsType, kTitlingCapsSelector, data); + return; + // TODO make an array? + case uiAttributeHanCharacterForms: + switch (spec->Value) { + case uiAttributeHanCharacterFormTraditional: + (*f)(kCharacterShapeType, kTraditionalCharactersSelector, data); + break; + case uiAttributeHanCharacterFormSimplified: + (*f)(kCharacterShapeType, kSimplifiedCharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS1978: + (*f)(kCharacterShapeType, kJIS1978CharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS1983: + (*f)(kCharacterShapeType, kJIS1983CharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS1990: + (*f)(kCharacterShapeType, kJIS1990CharactersSelector, data); + break; + case uiAttributeHanCharacterFormExpert: + (*f)(kCharacterShapeType, kExpertCharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS2004: + (*f)(kCharacterShapeType, kJIS2004CharactersSelector, data); + break; + case uiAttributeHanCharacterFormHojo: + (*f)(kCharacterShapeType, kHojoCharactersSelector, data); + break; + case uiAttributeHanCharacterFormNLC: + (*f)(kCharacterShapeType, kNLCCharactersSelector, data); + break; + case uiAttributeHanCharacterFormTraditionalNames: + (*f)(kCharacterShapeType, kTraditionalNamesCharactersSelector, data); + break; + } + return; + case uiAttributeLowercaseNumbers: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + (*f)(kNumberCaseType, kLowerCaseNumbersSelector, data); + return; + case uiAttributeHanjaToHangul: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + (*f)(kTransliterationType, kHanjaToHangulSelector, data); + return; + case uiAttributeGlyphAnnotations: + (*f)(kAnnotationType, (uint16_t) (spec->Value), data); + return; + case uiAttributeRubyKanaForms: + // include this for completeness + boolspec(spec, kRubyKanaType, + kRubyKanaSelector, + kNoRubyKanaSelector, + f, data); + // this is the current one + boolspec(spec, kRubyKanaType, + kRubyKanaOnSelector, + kRubyKanaOffSelector, + f, data); + return; + case uiAttributeCJKRomanToitalics: + // include this for completeness + boolspec(spec, kItalicCJKRomanType, + kCJKItalicRomanSelector, + kNoCJKItalicRomanSelector, + f, data); + // this is the current one + boolspec(spec, kItalicCJKRomanType, + kCJKItalicRomanOnSelector, + kCJKItalicRomanOffSelector, + f, data); + return; + case uiAttributeCaseSensitiveForms: + boolspec(spec, kCaseSensitiveLayoutType, + kCaseSensitiveLayoutOnSelector, + kCaseSensitiveLayoutOffSelector, + f, data); + return; + case uiAttributeCapitalSpacing: + boolspec(spec, kCaseSensitiveLayoutType, + kCaseSensitiveSpacingOnSelector, + kCaseSensitiveSpacingOffSelector, + f, data); + return; + case uiAttributeAlternateHorizontalKana: + boolspec(spec, kAlternateKanaType, + kAlternateHorizKanaOnSelector, + kAlternateHorizKanaOffSelector, + f, data); + return; + case uiAttributeAlternateVerticalKana: + boolspec(spec, kAlternateKanaType, + kAlternateVertKanaOnSelector, + kAlternateVertKanaOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative1: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltOneOnSelector, + kStylisticAltOneOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative2: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTwoOnSelector, + kStylisticAltTwoOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative3: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltThreeOnSelector, + kStylisticAltThreeOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative4: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFourOnSelector, + kStylisticAltFourOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative5: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFiveOnSelector, + kStylisticAltFiveOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative6: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSixOnSelector, + kStylisticAltSixOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative7: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSevenOnSelector, + kStylisticAltSevenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative8: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltEightOnSelector, + kStylisticAltEightOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative9: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltNineOnSelector, + kStylisticAltNineOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative10: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTenOnSelector, + kStylisticAltTenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative11: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltElevenOnSelector, + kStylisticAltElevenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative12: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTwelveOnSelector, + kStylisticAltTwelveOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative13: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltThirteenOnSelector, + kStylisticAltThirteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative14: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFourteenOnSelector, + kStylisticAltFourteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative15: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFifteenOnSelector, + kStylisticAltFifteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative16: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSixteenOnSelector, + kStylisticAltSixteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative17: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSeventeenOnSelector, + kStylisticAltSeventeenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative18: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltEighteenOnSelector, + kStylisticAltEighteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative19: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltNineteenOnSelector, + kStylisticAltNineteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative20: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTwentyOnSelector, + kStylisticAltTwentyOffSelector, + f, data); + return; + case uiAttributeContextualAlternates: + boolspec(spec, kContextualAlternatesType, + kContextualAlternatesOnSelector, + kContextualAlternatesOffSelector, + f, data); + return; + case uiAttributeSwashes: + boolspec(spec, kContextualAlternatesType, + kSwashAlternatesOnSelector, + kSwashAlternatesOffSelector, + f, data); + return; + case uiAttributeContextualSwashes: + boolspec(spec, kContextualAlternatesType, + kContextualSwashAlternatesOnSelector, + kContextualSwashAlternatesOffSelector, + f, data); + return; + // TODO use arrays? + case uiAttributeLowercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormNone: + (*f)(kLowerCaseType, kDefaultLowerCaseSelector, data); + break; + case uiAttributeCapFormSmallCaps: + // include this for compatibility (some fonts that come with OS X still use this!) + // TODO make it boolean? + (*f)(kLetterCaseType, kSmallCapsSelector, data); + // this is the current one + (*f)(kLowerCaseType, kLowerCaseSmallCapsSelector, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)(kLowerCaseType, kLowerCasePetiteCapsSelector, data); + break; + } + return; + // TODO use arrays? + case uiAttributeUppercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormNone: + (*f)(kUpperCaseType, kDefaultUpperCaseSelector, data); + break; + case uiAttributeCapFormSmallCaps: + (*f)(kUpperCaseType, kUpperCaseSmallCapsSelector, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)(kUpperCaseType, kUpperCasePetiteCapsSelector, data); + break; + } + return; + } +} From 1eb2ffaf828f091cbd92d61fa1b580fcac5bd813 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 21:30:21 -0500 Subject: [PATCH 168/487] FLIPPED THE SWITCH --- ui_attrstr.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index fac03b0f..31ac8f6b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -42,8 +42,6 @@ _UI_ENUM(uiAttribute) { // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility uiAttributeLanguage, // BCP 47 string -#if 0 - // These attributes represent typographic features. Each feature // is a separate attribute, to make composition easier. The // availability of for each attribute are defined by the font; the @@ -192,8 +190,6 @@ _UI_ENUM(uiAttribute) { // TODO kCJKRomanSpacingType -#endif - // TODO uiAttributeSystem, (this might not be doable with DirectWrite) // TODO uiAttributeCustom, }; From 3e941d008eee6261db5e32a2d167ff4d8538120c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 23:10:23 -0500 Subject: [PATCH 169/487] Integrated aat.m into the build. --- common/opentype.c | 4 +- darwin/CMakeLists.txt | 1 + darwin/aat.m | 111 ++++++++++++++++++++--------------------- darwin/attrstr.m | 30 +++++++++++ darwin/uipriv_darwin.h | 4 ++ 5 files changed, 92 insertions(+), 58 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index f874752b..4c5e2070 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -55,7 +55,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeNumberSpacingProportional: (*f)("pnum", 1, data); break; - case uiAttributeNumberSpacingTitling: + case uiAttributeNumberSpacingTabular: (*f)("tnum", 1, data); break; } @@ -152,7 +152,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeRubyKanaForms: boolspec(spec, "ruby", data); return; - case uiAttributeCJKRomanToitalics: + case uiAttributeCJKRomansToItalics: boolspec(spec, "ital", data); return; case uiAttributeCaseSensitiveForms: diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 7d9ead75..96db9c09 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -1,6 +1,7 @@ # 3 june 2016 list(APPEND _LIBUI_SOURCES + darwin/aat.m darwin/alloc.m darwin/area.m darwin/areaevents.m diff --git a/darwin/aat.m b/darwin/aat.m index 05622f3c..9aab5f89 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,8 +1,6 @@ // 14 february 2017 #import "uipriv_darwin.h" -typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); - static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, specToAATEnumFunc f, void *data) { if (spec->Value != 0) { @@ -12,7 +10,7 @@ static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint (*f)(type, ifFalse, data); } -void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) +int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) { switch (spec->Type) { case uiAttributeStandardLigatures: @@ -20,48 +18,48 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kCommonLigaturesOnSelector, kCommonLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeRequiredLigatures: boolspec(spec, kLigaturesType, kRequiredLigaturesOnSelector, kRequiredLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeDiscretionaryLigatures: boolspec(spec, kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeContextualLigatures: boolspec(spec, kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeHistoricalLigatures: boolspec(spec, kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeUnicase: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table (*f)(kLetterCaseType, 14, data); - return; + return 1; // TODO make an array? case uiAttributeNumberSpacings: switch (spec->Value) { case uiAttributeNumberSpacingProportional: (*f)(kNumberSpacingType, kProportionalNumbersSelector, data); break; - case uiAttributeNumberSpacingTitling: + case uiAttributeNumberSpacingTabular: (*f)(kNumberSpacingType, kMonospacedNumbersSelector, data); break; } - return; + return 1; // TODO make an array? case uiAttributeSuperscripts: switch (spec->Value) { @@ -81,7 +79,7 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kVerticalPositionType, kScientificInferiorsSelector, data); break; } - return; + return 1; // TODO make an array? case uiAttributeFractionForms: switch (spec->Value) { @@ -95,30 +93,30 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kFractionsType, kDiagonalFractionsSelector, data); break; } - return; + return 1; case uiAttributeSlashedZero: boolspec(spec, kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector, f, data); - return; + return 1; case uiAttributeMathematicalGreek: boolspec(spec, kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector, f, data); - return; + return 1; case uiAttributeOrnamentalForms: (*f)(kOrnamentSetsType, (uint16_t) (spec->Value), data); - return; + return 1; case uiAttributeSpecificCharacterForm: (*f)(kCharacterAlternativesType, (uint16_t) (spec->Value), data); - return; + return 1; case uiAttributeTitlingCapitalForms: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) (*f)(kStyleOptionsType, kTitlingCapsSelector, data); - return; + return 1; // TODO make an array? case uiAttributeHanCharacterForms: switch (spec->Value) { @@ -153,20 +151,20 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kCharacterShapeType, kTraditionalNamesCharactersSelector, data); break; } - return; + return 1; case uiAttributeLowercaseNumbers: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) (*f)(kNumberCaseType, kLowerCaseNumbersSelector, data); - return; + return 1; case uiAttributeHanjaToHangul: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) (*f)(kTransliterationType, kHanjaToHangulSelector, data); - return; + return 1; case uiAttributeGlyphAnnotations: (*f)(kAnnotationType, (uint16_t) (spec->Value), data); - return; + return 1; case uiAttributeRubyKanaForms: // include this for completeness boolspec(spec, kRubyKanaType, @@ -178,8 +176,8 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kRubyKanaOnSelector, kRubyKanaOffSelector, f, data); - return; - case uiAttributeCJKRomanToitalics: + return 1; + case uiAttributeCJKRomansToItalics: // include this for completeness boolspec(spec, kItalicCJKRomanType, kCJKItalicRomanSelector, @@ -190,173 +188,173 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector, f, data); - return; + return 1; case uiAttributeCaseSensitiveForms: boolspec(spec, kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector, f, data); - return; + return 1; case uiAttributeCapitalSpacing: boolspec(spec, kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector, f, data); - return; + return 1; case uiAttributeAlternateHorizontalKana: boolspec(spec, kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, f, data); - return; + return 1; case uiAttributeAlternateVerticalKana: boolspec(spec, kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative1: boolspec(spec, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative2: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative3: boolspec(spec, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative4: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative5: boolspec(spec, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative6: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative7: boolspec(spec, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative8: boolspec(spec, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative9: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative10: boolspec(spec, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative11: boolspec(spec, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative12: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative13: boolspec(spec, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative14: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative15: boolspec(spec, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative16: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative17: boolspec(spec, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative18: boolspec(spec, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative19: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative20: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, f, data); - return; + return 1; case uiAttributeContextualAlternates: boolspec(spec, kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector, f, data); - return; + return 1; case uiAttributeSwashes: boolspec(spec, kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector, f, data); - return; + return 1; case uiAttributeContextualSwashes: boolspec(spec, kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector, f, data); - return; + return 1; // TODO use arrays? case uiAttributeLowercaseCapForms: switch (spec->Value) { - case uiAttributeCapFormNone: + case uiAttributeCapFormNormal: (*f)(kLowerCaseType, kDefaultLowerCaseSelector, data); break; case uiAttributeCapFormSmallCaps: @@ -370,11 +368,11 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kLowerCaseType, kLowerCasePetiteCapsSelector, data); break; } - return; + return 1; // TODO use arrays? case uiAttributeUppercaseCapForms: switch (spec->Value) { - case uiAttributeCapFormNone: + case uiAttributeCapFormNormal: (*f)(kUpperCaseType, kDefaultUpperCaseSelector, data); break; case uiAttributeCapFormSmallCaps: @@ -384,6 +382,7 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kUpperCaseType, kUpperCasePetiteCapsSelector, data); break; } - return; + return 1; } + return 0; } diff --git a/darwin/attrstr.m b/darwin/attrstr.m index b46872ce..c7e227cf 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -139,6 +139,27 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } +struct aatParam { + struct foreachParams *p; + size_t start; + size_t end; +}; + +static void doAAT(uint16_t type, uint16_t selector, void *data) +{ + struct aatParam *p = (struct aatParam *) data; + + ensureFontInRange(p->p, p->start, p->end); + adjustFontInRange(p->p, p->start, p->end, ^(struct fontParams *fp) { + fp->featureTypes[fp->nFeatures] = type; + fp->featureSelectors[fp->nFeatures] = selector; + fp->nFeatures++; + if (fp->nFeatures == maxFeatures) { + // TODO + } + }); +} + static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -148,6 +169,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t backgroundBlock block; int32_t us; CFNumberRef num; + struct aatParam ap; ostart = start; oend = end; @@ -251,6 +273,14 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t }); break; // TODO + default: + // handle typographic features + ap.p = p; + ap.start = start; + ap.end = end; + // TODO check if unhandled and complain + specToAAT(spec, doAAT, &ap); + break; } return 0; } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 9d662cd5..281a4b57 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -150,3 +150,7 @@ extern void initUnderlineColors(void); extern void uninitUnderlineColors(void); typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); + +// aat.m +typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); +extern int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data); From dcc01f5b01401d454a851b363578141442bf6dda Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 23:52:16 -0500 Subject: [PATCH 170/487] Started putting the typographical features attributes into the example program. --- examples/drawtext/attributes.c | 69 +++++++++++++++++++++++++++++++++- examples/drawtext/main.c | 7 ++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 9d0c2310..47601933 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -165,7 +165,74 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "multiple TODO"; + next = "or any combination of the above"; + // TODO + + uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); + + next = "fi"; + uiAttributedStringAppendUnattributed(attrstr, "standard ligatures like f+i ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeStandardLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + // note the use of RTL embeds to make sure the bidi algorithm doesn't kick in for our demonstration (it will produce incorrect results) + // see also: https://www.w3.org/International/articles/inline-bidi-markup/#nomarkup + next = "\xD9\x84\xD8\xA7"; + uiAttributedStringAppendUnattributed(attrstr, "required ligatures like \xE2\x80\xAB\xD9\x84\xE2\x80\xAC+\xE2\x80\xAB\xD8\xA7\xE2\x80\xAC (\xE2\x80\xAB"); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeRequiredLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, "\xE2\x80\xAC)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "ct"; + uiAttributedStringAppendUnattributed(attrstr, "discretionary/rare ligatures like c+t ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeDiscretionaryLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "the"; + uiAttributedStringAppendUnattributed(attrstr, "contextual ligatures like h+e in the ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xC3\x9F"; + uiAttributedStringAppendUnattributed(attrstr, "historical ligatures like the decomposition of \xC3\x9F ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHistoricalLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + // TODO unicase } static char fontFamily[] = "Times New Roman"; diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 848def89..22b532fe 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -5,6 +5,13 @@ // - occasional segfaults on startup // - very rare size attributes in the attributed string example don't terminate for a while, making everything big // - very very rare trace/bpt faults on startup +/* +objc[14827]: autorelease pool page 0x7feeab88b000 corrupted + magic 0xe000007f 0xeea9f2df 0x0000007f 0xeea9f2e0 + should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341 + pthread 0x0 + should be 0x7fff727a1000 +*/ static uiWindow *mainwin; static uiBox *box; From 4262d893bc4923eebb8541f28a20d72fcc5ba22b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 00:13:09 -0500 Subject: [PATCH 171/487] Fixed the Arabic embeds that demonstrate required ligatures. --- examples/drawtext/attributes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 47601933..c7ecf880 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -182,10 +182,10 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - // note the use of RTL embeds to make sure the bidi algorithm doesn't kick in for our demonstration (it will produce incorrect results) + // note the use of LTR marks and RTL embeds to make sure the bidi algorithm doesn't kick in for our demonstration (it will produce incorrect results) // see also: https://www.w3.org/International/articles/inline-bidi-markup/#nomarkup next = "\xD9\x84\xD8\xA7"; - uiAttributedStringAppendUnattributed(attrstr, "required ligatures like \xE2\x80\xAB\xD9\x84\xE2\x80\xAC+\xE2\x80\xAB\xD8\xA7\xE2\x80\xAC (\xE2\x80\xAB"); + uiAttributedStringAppendUnattributed(attrstr, "required ligatures like \xE2\x80\xAB\xD9\x84\xE2\x80\xAC+\xE2\x80\xAB\xD8\xA7\xE2\x80\xAC (\xE2\x80\x8E\xE2\x80\xAB"); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); From 768aba6614e7fa4a0916414689c6bf2afc9ae8fc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 03:05:29 -0500 Subject: [PATCH 172/487] Added test of Unicase. Had to use the Arial that comes with Windows 10, which seems to be the only font file I have out of **everything** that comes with Unicase. :S --- examples/drawtext/attributes.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c7ecf880..51c6cbfa 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -232,7 +232,17 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - // TODO unicase + next = "UnICasE wRITInG"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnicase; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "TODO"; } static char fontFamily[] = "Times New Roman"; From 685e17fc0e73cc22c326a91c57d29d2e3b398d43 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 12:49:06 -0500 Subject: [PATCH 173/487] More typographical features examples. This crashing thing is getting really annoying. --- examples/drawtext/attributes.c | 115 ++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 51c6cbfa..8afbb2d7 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -232,6 +232,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + // TODO a different word than "writing"? next = "UnICasE wRITInG"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); @@ -242,7 +243,119 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "TODO"; + next = "316"; + uiAttributedStringAppendUnattributed(attrstr, "proportional ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeNumberSpacings; + spec.Value = uiAttributeNumberSpacingProportional; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") and tabular/monospaced ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeNumberSpacings; + spec.Value = uiAttributeNumberSpacingTabular; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") numbers"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "123"; + uiAttributedStringAppendUnattributed(attrstr, "superscipts ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptSuperscript; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "123"; + uiAttributedStringAppendUnattributed(attrstr, "subscripts ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptSubscript; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "1st"; + uiAttributedStringAppendUnattributed(attrstr, "ordinals ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptOrdinal; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "H2O"; + uiAttributedStringAppendUnattributed(attrstr, "scientific inferiors ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptScientificInferior; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "2/3"; + uiAttributedStringAppendUnattributed(attrstr, "fraction forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeFractionForms; + spec.Value = uiAttributeFractionFormNone; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeFractionForms; + spec.Value = uiAttributeFractionFormVertical; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeFractionForms; + spec.Value = uiAttributeFractionFormDiagonal; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "0"; + uiAttributedStringAppendUnattributed(attrstr, "slashed zeroes ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSlashedZero; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSlashedZero; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "TODO mathematical greek"; } static char fontFamily[] = "Times New Roman"; From 92b860c8f4091407ae02ac71a1df5ff5613f37d2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 13:00:57 -0500 Subject: [PATCH 174/487] Added Address Sanitizer as a cmake option. This will help us figure out the crash. --- CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb1696d9..f4a01049 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,18 @@ else() -static-libstdc++ ) endif() + + # TODO document this + if(ADDRESS_SANITIZER) + set(_COMMON_CFLAGS ${_COMMON_CFLAGS} + -fsanitize=address + -fno-omit-frame-pointer + ) + set(_COMMON_LDFLAGS ${_COMMON_LDFLAGS} + -fsanitize=address + -fno-omit-frame-pointer + ) + endif() endif() # problem: From e27e51c4b889e4ec536a1d091e27c4b77b083a26 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 14:25:04 -0500 Subject: [PATCH 175/487] Seemed to fix crashing issues for now. Character insertion is borked :| --- common/attrstr.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 9c12a532..c5466edf 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -93,6 +93,7 @@ static void u8u16len(const char *str, size_t *n8, size_t *n16) *n16 = 0; while (*str) { str = utf8DecodeRune(str, 0, &rune); + // TODO document the use of the function vs a pointer subtract here *n8 += utf8EncodeRune(rune, buf); *n16 += utf16EncodeRune(rune, buf16); } @@ -123,6 +124,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s uint16_t buf16[2]; size_t n8, n16; // TODO make loop-local? to avoid using them in the wrong place again size_t old, old16; + size_t oldn8, oldn16; size_t oldlen, old16len; size_t at16; size_t i; @@ -168,6 +170,8 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u16tou8 + at16 + n16, s->u16tou8 + at16, (old16len - at16 + 1) * sizeof (size_t)); + oldn8 = n8; + oldn16 = n16; // and copy while (*str) { @@ -201,15 +205,15 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s } // and have an index for the end of the string // TODO is this done by the below? - s->u8tou16[old] = old16; - s->u16tou8[old16] = old; +//TODO s->u8tou16[old] = old16; +//TODO s->u16tou8[old16] = old; // and adjust the prior values in the conversion tables // use <= so the terminating 0 gets updated too for (i = 0; i <= oldlen - at; i++) - s->u8tou16[at + oldlen + i] += old16len; + s->u8tou16[at + oldn8 + i] += s->u16len - old16len; for (i = 0; i <= old16len - at16; i++) - s->u16tou8[at16 + old16len + i] += oldlen; + s->u16tou8[at16 + oldn16 + i] += s->len - oldlen; // and finally do the attributes attrlistInsertCharactersUnattributed(s->attrs, at, n8); From 025dd16d7600b37d0ecf4ec1f14871c38ecaeecc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 15:02:19 -0500 Subject: [PATCH 176/487] More examples and crash fixes. --- darwin/fontmatch.m | 93 +++++++++++++++++----------------- examples/drawtext/attributes.c | 56 +++++++++++++++++++- 2 files changed, 102 insertions(+), 47 deletions(-) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 8d5094a7..9acb2edf 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -298,56 +298,57 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 } // now we have to take care of the language - // TODO can we assume this is present? if (language != NULL) { languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); - nl = CFArrayGetCount(languages); - d[0] = language[0]; - if (d[0] >= 'A' && d[0] <= 'Z') - d[0] += 'a' - 'A'; - d[1] = language[1]; - if (d[1] >= 'A' && d[1] <= 'Z') - d[1] += 'a' - 'A'; - for (il = 0; il < nl; il++) { - char c[2]; - - curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); - // TODO check for failure - CFStringGetBytes(curlang, CFRangeMake(0, 2), - kCFStringEncodingUTF8, 0, false, - (UInt8 *) c, 2, NULL); - if (c[0] >= 'A' && c[0] <= 'Z') - c[0] += 'a' - 'A'; - if (c[1] >= 'A' && c[1] <= 'Z') - c[1] += 'a' - 'A'; - if (c[0] == d[0] && c[1] == d[1]) - break; - } - if (il != nl) { - uint16_t typ; - - typ = kLanguageTagType; - il++; - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&typ)); - numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, - &il); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO + if (languages != NULL) { + nl = CFArrayGetCount(languages); + d[0] = language[0]; + if (d[0] >= 'A' && d[0] <= 'Z') + d[0] += 'a' - 'A'; + d[1] = language[1]; + if (d[1] >= 'A' && d[1] <= 'Z') + d[1] += 'a' - 'A'; + for (il = 0; il < nl; il++) { + char c[2]; + + curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); + // TODO check for failure + CFStringGetBytes(curlang, CFRangeMake(0, 2), + kCFStringEncodingUTF8, 0, false, + (UInt8 *) c, 2, NULL); + if (c[0] >= 'A' && c[0] <= 'Z') + c[0] += 'a' - 'A'; + if (c[1] >= 'A' && c[1] <= 'Z') + c[1] += 'a' - 'A'; + if (c[0] == d[0] && c[1] == d[1]) + break; } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); + if (il != nl) { + uint16_t typ; + + typ = kLanguageTagType; + il++; + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&typ)); + numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, + &il); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + CFRelease(languages); } - CFRelease(languages); } keys[0] = kCTFontFeatureSettingsAttribute; diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 8afbb2d7..625991c1 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -8,6 +8,7 @@ static void setupAttributedString(void) uiAttributeSpec spec; size_t start, end; const char *next; + int i; attrstr = uiNewAttributedString("uiAttributedString isn't just for plain text! It supports "); @@ -355,7 +356,60 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "TODO mathematical greek"; + next = "\xCE\xA0\xCE\xA3"; + uiAttributedStringAppendUnattributed(attrstr, "mathematical greek ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeMathematicalGreek; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeMathematicalGreek; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "qwertyuiop\xE2\x80\xA2"; + uiAttributedStringAppendUnattributed(attrstr, "ornamental forms ("); + for (i = 1; i < 11; i++) { + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeOrnamentalForms; + spec.Value = (uintptr_t) i; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + next = "\xE2\x80\xA2"; + } + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "g"; + uiAttributedStringAppendUnattributed(attrstr, "specific forms/alternates ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSpecificCharacterForm; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSpecificCharacterForm; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "TODO titling capitals"; } static char fontFamily[] = "Times New Roman"; From fbd294c089ce147d64a862250e3a939c6dd887d8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 12:38:39 -0500 Subject: [PATCH 177/487] Filled in the rest of the typographic features. Now to write the any combination example. --- examples/drawtext/attributes.c | 325 ++++++++++++++++++++++++++++++++- ui_attrstr.h | 1 + 2 files changed, 324 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 625991c1..ef77a29d 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -3,6 +3,7 @@ static uiAttributedString *attrstr; +// some of these examples come from Microsoft's and Apple's lists of typographic features and also https://www.fontfont.com/staticcontent/downloads/FF_OT_User_Guide.pdf static void setupAttributedString(void) { uiAttributeSpec spec; @@ -167,7 +168,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); next = "or any combination of the above"; - // TODO + // TODOTODO uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); @@ -409,7 +410,327 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "TODO titling capitals"; + next = "ABCDEFGQWERTY"; + uiAttributedStringAppendUnattributed(attrstr, "titling capital forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeTitlingCapitalForms; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeTitlingCapitalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE7\x80\x86"; + uiAttributedStringAppendUnattributed(attrstr, "alternate Han character forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanCharacterForms; + spec.Value = uiAttributeHanCharacterFormJIS1978; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanCharacterForms; + spec.Value = uiAttributeHanCharacterFormJIS1983; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "0123456789"; + uiAttributedStringAppendUnattributed(attrstr, "lowercase numbers ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseNumbers; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseNumbers; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE4\xBC\xBD"; + uiAttributedStringAppendUnattributed(attrstr, "hanja to hangul translation ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanjaToHangul; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanjaToHangul; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE3\x81\x82"; + uiAttributedStringAppendUnattributed(attrstr, "annotated glyph forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeGlyphAnnotations; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeGlyphAnnotations; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeGlyphAnnotations; + spec.Value = 4; // AAT inverted circle + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE3\x81\x82"; + uiAttributedStringAppendUnattributed(attrstr, "ruby forms of kana ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeRubyKanaForms; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeRubyKanaForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "now is the time"; + uiAttributedStringAppendUnattributed(attrstr, "italic forms of Latin letters in CJK fonts ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCJKRomansToItalics; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCJKRomansToItalics; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "{I} > {J}"; + uiAttributedStringAppendUnattributed(attrstr, "case-sensitive character forms, such as punctuation ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCaseSensitiveForms; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCaseSensitiveForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "ABC"; + uiAttributedStringAppendUnattributed(attrstr, "specialized spacing between uppercase letters ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCapitalSpacing; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCapitalSpacing; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE3\x82\xB9\xE3\x83\x98\xE3\x83\x88"; + uiAttributedStringAppendUnattributed(attrstr, "alternate horizontal ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateHorizontalKana; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateHorizontalKana; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") and vertical ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateVerticalKana; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateVerticalKana; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") kana forms"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "g"; + uiAttributedStringAppendUnattributed(attrstr, "stylistic alternates ("); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeStylisticAlternative1; + spec.Value = 1; + for (i = 0; i < 20; i++) { + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type++; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + } + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "first"; + uiAttributedStringAppendUnattributed(attrstr, "contextual alternates ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualAlternates; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualAlternates; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "FONT"; + uiAttributedStringAppendUnattributed(attrstr, "swashes ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSwashes; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSwashes; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "Font"; + uiAttributedStringAppendUnattributed(attrstr, "contextual swashes ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualSwashes; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualSwashes; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "Small Caps"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseCapForms; + spec.Value = uiAttributeCapFormSmallCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "Petite Caps"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseCapForms; + spec.Value = uiAttributeCapFormPetiteCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "SMALL UPPERCASES"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUppercaseCapForms; + spec.Value = uiAttributeCapFormSmallCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", and "); + next = "PETITE UPPERCASES"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUppercaseCapForms; + spec.Value = uiAttributeCapFormPetiteCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, "."); + + // TODO write a dedicated example for experimenting with typographic features like the one in gtk3-demo } static char fontFamily[] = "Times New Roman"; diff --git a/ui_attrstr.h b/ui_attrstr.h index 31ac8f6b..5e4fe8a8 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -160,6 +160,7 @@ _UI_ENUM(uiAttribute) { uiAttributeAlternateVerticalKana, // 0 = off, 1 = on // TODO "Alternate"? unify all this + // TODO document that these are guaranteed to be consecutive uiAttributeStylisticAlternative1, // 0 = off, 1 = on uiAttributeStylisticAlternative2, // 0 = off, 1 = on uiAttributeStylisticAlternative3, // 0 = off, 1 = on From f65d4592d0507597c65f9620d83a8c744bea869f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 12:49:20 -0500 Subject: [PATCH 178/487] Combined attributes. We're good I guess. --- examples/drawtext/attributes.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index ef77a29d..c0a4e5e9 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -167,8 +167,36 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + // TODO randomize these ranges better + // TODO also change colors to light foreground dark background next = "or any combination of the above"; - // TODOTODO + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeWeight; + spec.Value = (uintptr_t) uiDrawTextWeightBold; + uiAttributedStringSetAttribute(attrstr, &spec, start, end - 8); + spec.Type = uiAttributeItalic; + spec.Value = (uintptr_t) uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start + 3, end - 4); + spec.Type = uiAttributeColor; + spec.R = 0.8627450980392156; + spec.G = 0.0784313725490196; + spec.B = 0.2352941176470588; + spec.A = 0.75; + uiAttributedStringSetAttribute(attrstr, &spec, start + 12, end); + spec.Type = uiAttributeFamily; + spec.Value = (uintptr_t) "Helvetica"; + uiAttributedStringSetAttribute(attrstr, &spec, start + 8, end - 1); + spec.Type = uiAttributeBackground; + spec.R = 1.0; + spec.G = 0.85490196078431372; + spec.B = 0.7254901960784313; + spec.A = 0.5; + uiAttributedStringSetAttribute(attrstr, &spec, start + 5, end - 7); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSingle; + uiAttributedStringSetAttribute(attrstr, &spec, start + 9, end - 1); uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); From 7920559aa5a3279f16a5ecf2510ca8622a8dcf05 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 12:50:00 -0500 Subject: [PATCH 179/487] More TODOs. --- examples/drawtext/attributes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c0a4e5e9..f0868a8b 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -168,6 +168,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); // TODO randomize these ranges better + // TODO make some overlap to test that // TODO also change colors to light foreground dark background next = "or any combination of the above"; start = uiAttributedStringLen(attrstr); From 6ae6e91238757d222fd120d77b5c200e2942351d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 14:59:43 -0500 Subject: [PATCH 180/487] Integrated opentype.c into the build. Finally. --- common/CMakeLists.txt | 1 + common/opentype.c | 70 +++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 8ecff8b3..c0542c82 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND _LIBUI_SOURCES common/debug.c common/drawtext.c common/matrix.c + common/opentype.c common/shouldquit.c common/userbugs.c common/utf.c diff --git a/common/opentype.c b/common/opentype.c index 4c5e2070..aa0a389d 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -88,10 +88,10 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) } return; case uiAttributeSlashedZero: - boolspec(spec, "zero", data); + boolspec(spec, "zero", f, data); return; case uiAttributeMathematicalGreek: - boolspec(spec, "mgrk", data); + boolspec(spec, "mgrk", f, data); return; case uiAttributeOrnamentalForms: (*f)("ornm", (uint32_t) (spec->Value), data); @@ -100,7 +100,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) (*f)("aalt", (uint32_t) (spec->Value), data); return; case uiAttributeTitlingCapitalForms: - boolspec(spec, "titl", data); + boolspec(spec, "titl", f, data); return; // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeHanCharacterForms: @@ -138,103 +138,103 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) } return; case uiAttributeLowercaseNumbers: - boolspec(spec, "onum", data); + boolspec(spec, "onum", f, data); // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too // TODO is it always set? - boolspecnot(spec, "lnum", data); + boolspecnot(spec, "lnum", f, data); return; case uiAttributeHanjaToHangul: - boolspec(spec, "hngl", data); + boolspec(spec, "hngl", f, data); return; case uiAttributeGlyphAnnotations: (*f)("nalt", (uint32_t) (spec->Value), data); return; case uiAttributeRubyKanaForms: - boolspec(spec, "ruby", data); + boolspec(spec, "ruby", f, data); return; case uiAttributeCJKRomansToItalics: - boolspec(spec, "ital", data); + boolspec(spec, "ital", f, data); return; case uiAttributeCaseSensitiveForms: - boolspec(spec, "case", data); + boolspec(spec, "case", f, data); return; case uiAttributeCapitalSpacing: - boolspec(spec, "cpsp", data); + boolspec(spec, "cpsp", f, data); return; case uiAttributeAlternateHorizontalKana: - boolspec(spec, "hkna", data); + boolspec(spec, "hkna", f, data); return; case uiAttributeAlternateVerticalKana: - boolspec(spec, "vkna", data); + boolspec(spec, "vkna", f, data); return; case uiAttributeStylisticAlternative1: - boolspec(spec, "ss01", data); + boolspec(spec, "ss01", f, data); return; case uiAttributeStylisticAlternative2: - boolspec(spec, "ss02", data); + boolspec(spec, "ss02", f, data); return; case uiAttributeStylisticAlternative3: - boolspec(spec, "ss03", data); + boolspec(spec, "ss03", f, data); return; case uiAttributeStylisticAlternative4: - boolspec(spec, "ss04", data); + boolspec(spec, "ss04", f, data); return; case uiAttributeStylisticAlternative5: - boolspec(spec, "ss05", data); + boolspec(spec, "ss05", f, data); return; case uiAttributeStylisticAlternative6: - boolspec(spec, "ss06", data); + boolspec(spec, "ss06", f, data); return; case uiAttributeStylisticAlternative7: - boolspec(spec, "ss07", data); + boolspec(spec, "ss07", f, data); return; case uiAttributeStylisticAlternative8: - boolspec(spec, "ss08", data); + boolspec(spec, "ss08", f, data); return; case uiAttributeStylisticAlternative9: - boolspec(spec, "ss09", data); + boolspec(spec, "ss09", f, data); return; case uiAttributeStylisticAlternative10: - boolspec(spec, "ss10", data); + boolspec(spec, "ss10", f, data); return; case uiAttributeStylisticAlternative11: - boolspec(spec, "ss11", data); + boolspec(spec, "ss11", f, data); return; case uiAttributeStylisticAlternative12: - boolspec(spec, "ss12", data); + boolspec(spec, "ss12", f, data); return; case uiAttributeStylisticAlternative13: - boolspec(spec, "ss13", data); + boolspec(spec, "ss13", f, data); return; case uiAttributeStylisticAlternative14: - boolspec(spec, "ss14", data); + boolspec(spec, "ss14", f, data); return; case uiAttributeStylisticAlternative15: - boolspec(spec, "ss15", data); + boolspec(spec, "ss15", f, data); return; case uiAttributeStylisticAlternative16: - boolspec(spec, "ss16", data); + boolspec(spec, "ss16", f, data); return; case uiAttributeStylisticAlternative17: - boolspec(spec, "ss17", data); + boolspec(spec, "ss17", f, data); return; case uiAttributeStylisticAlternative18: - boolspec(spec, "ss18", data); + boolspec(spec, "ss18", f, data); return; case uiAttributeStylisticAlternative19: - boolspec(spec, "ss19", data); + boolspec(spec, "ss19", f, data); return; case uiAttributeStylisticAlternative20: - boolspec(spec, "ss20", data); + boolspec(spec, "ss20", f, data); return; case uiAttributeContextualAlternates: - boolspec(spec, "calt", data); + boolspec(spec, "calt", f, data); return; case uiAttributeSwashes: - boolspec(spec, "swsh", data); + boolspec(spec, "swsh", f, data); return; case uiAttributeContextualSwashes: - boolspec(spec, "cswh", data); + boolspec(spec, "cswh", f, data); return; // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeLowercaseCapForms: From ea473a341187c71fbe2685f7e6009be041f7c5f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 15:14:53 -0500 Subject: [PATCH 181/487] Ugh of course I screwed up the malloc() test. Fixed a crash on GTK+ since I guess OS X malloc() autofills to 0? --- darwin/alloc.m | 2 +- unix/alloc.c | 2 +- windows/alloc.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/darwin/alloc.m b/darwin/alloc.m index e271b90e..0bbb8996 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -71,7 +71,7 @@ void *uiRealloc(void *p, size_t new, const char *type) abort(); } s = SIZE(out); - if (new <= *s) + if (new > *s) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/unix/alloc.c b/unix/alloc.c index 2561efa6..0291bd49 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -64,7 +64,7 @@ void *uiRealloc(void *p, size_t new, const char *type) p = BASE(p); out = g_realloc(p, EXTRA + new); s = SIZE(out); - if (new <= *s) + if (new > *s) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; if (g_ptr_array_remove(allocations, p) == FALSE) diff --git a/windows/alloc.cpp b/windows/alloc.cpp index eeee3ad4..23201f75 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -45,6 +45,7 @@ void *uiRealloc(void *_p, size_t size, const char *type) if (p == NULL) return uiAlloc(size, type); arr = heap[p]; + // TODO does this fill in? arr->resize(size, 0); heap.erase(p); heap[rawBytes(arr)] = arr; From d7a44a51688936ba7e6a8b08ee33b5de003d9952 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 17:11:52 -0500 Subject: [PATCH 182/487] Added uiAttribute handling code to the GTK+ backend. Not finished yet; not used yet. --- common/opentype.c | 2 - common/uipriv.h | 4 + unix/CMakeLists.txt | 1 + unix/attrstr.c | 254 ++++++++++++++++++++++++++++++++++++++++++++ unix/future.c | 9 ++ unix/uipriv_unix.h | 5 + 6 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 unix/attrstr.c diff --git a/common/opentype.c b/common/opentype.c index aa0a389d..a3e4f852 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -5,8 +5,6 @@ // Notes: // - Each tag should only appear in quotes once (including within comments); this allows automated tools to determine what we cover and don't cover -typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); - static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) { if (spec->Value != 0) { diff --git a/common/uipriv.h b/common/uipriv.h index 553073f5..1c61f9c7 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -101,6 +101,10 @@ struct caretDrawParams { extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); +// opentype.c +typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); +extern void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data); + #ifdef __cplusplus } #endif diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 9300bcb7..949c4a11 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -6,6 +6,7 @@ pkg_check_modules(GTK REQUIRED gtk+-3.0) list(APPEND _LIBUI_SOURCES unix/alloc.c unix/area.c + unix/attrstr.c unix/box.c unix/button.c unix/cellrendererbutton.c diff --git a/unix/attrstr.c b/unix/attrstr.c new file mode 100644 index 00000000..12cbb12d --- /dev/null +++ b/unix/attrstr.c @@ -0,0 +1,254 @@ +// 12 february 2017 +#include "uipriv_unix.h" + +// we need to collect all the OpenType features and background blocks and add them all at once +// TODO rename this struct to something that isn't exclusively foreach-ing? +struct foreachParams { + PangoAttrList *attrs; + // keys are pointers to size_t maintained by g_new0()/g_free() + // values are GStrings + GHashTable *features; +//TODO GArray *backgroundBlocks; +}; + +static gboolean featurePosEqual(gconstpointer a, gconstpointer b) +{ + size_t *sa = (size_t *) a; + size_t *sb = (size_t *) b; + + return *sa == *sb; +} + +static guint featurePosHash(gconstpointer n) +{ + size_t *sn = (size_t *) n; + + return (guint) (*sn); +} + +static void freeFeatureString(gpointer s) +{ + g_string_free((GString *) s, TRUE); +} + +static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +{ + size_t i; + size_t *key; + GString *new; + + for (i = start; i < end; i++) { + new = (GString *) g_hash_table_lookup(p->features, &i); + if (new != NULL) + continue; + new = g_string_new(""); + key = g_new0(size_t, 1); + *key = i; + g_hash_table_replace(p->features, key, new); + } +} + +#if 0 /* TODO */ +static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) +{ + return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = r; + brush.G = g; + brush.B = b; + brush.A = a; + drawTextBackground(c, x, y, layout, start, end, &brush, 0); + }); +} +#endif + +struct otParam { + struct foreachParams *p; + size_t start; + size_t end; +}; + +// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings +static void doOpenType(const char *featureTag, uint32_t param, void *data) +{ + struct otParam *p = (struct otParam *) data; + size_t i; + GString *s; + + ensureFeaturesInRange(p->p, p->start, p->end); + for (i = p->start; i < p->end; i++) { + s = (GString *) g_hash_table_lookup(p->p->features, &i); + g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", + featureTag, param); + } +} + +static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) +{ + if (attr == NULL) // in case of a future attribute + return; + attr->start_index = start; + attr->end_index = end; + pango_attr_list_insert(p->attrs, attr); +} + +static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; +//TODO backgroundBlock block; + PangoGravity gravity; + PangoUnderline underline; + PangoLanguage *lang; + struct otParam op; + + switch (spec->Type) { + case uiAttributeFamily: + addattr(p, start, end, + pango_attr_family_new((const char *) (spec->Value))); + break; +#if 0 /* TODO */ + case uiAttributeSize: + addattr(p, start, end, + pango_attr_size_new(cairoToPango(spec->Double))); + break; + case uiAttributeWeight: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Weight = (uiDrawTextWeight) (spec->Value); + }); + break; + case uiAttributeItalic: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Italic = (uiDrawTextItalic) (spec->Value); + }); + break; + case uiAttributeStretch: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); + }); + break; +#endif + case uiAttributeColor: + addattr(p, start, end, + pango_attr_foreground_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_foreground_alpha_new( + (guint16) (spec->A * 65535.0))); + break; +#if 0 /* TODO */ + case uiAttributeBackground: + block = mkBackgroundBlock(ostart, oend, + spec->R, spec->G, spec->B, spec->A); + [p->backgroundBlocks addObject:block]; + Block_release(block); + break; +#endif + case uiAttributeVerticalForms: + gravity = PANGO_GRAVITY_SOUTH; + if (spec->Value != 0) + gravity = PANGO_GRAVITY_EAST; + addattr(p, start, end, + pango_attr_gravity_new(gravity)); + break; + case uiAttributeUnderline: + switch (spec->Value) { + case uiDrawUnderlineStyleNone: + underline = PANGO_UNDERLINE_NONE; + break; + case uiDrawUnderlineStyleSingle: + underline = PANGO_UNDERLINE_SINGLE; + break; + case uiDrawUnderlineStyleDouble: + underline = PANGO_UNDERLINE_DOUBLE; + break; + case uiDrawUnderlineStyleSuggestion: + underline = PANGO_UNDERLINE_ERROR; + break; + } + addattr(p, start, end, + pango_attr_underline_new(underline)); + break; + case uiAttributeUnderlineColor: + switch (spec->Value) { + case uiDrawUnderlineColorCustom: + addattr(p, start, end, + pango_attr_underline_color_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + break; + case uiDrawUnderlineColorSpelling: + // TODO GtkTextView style property error-underline-color + addattr(p, start, end, + pango_attr_underline_color_new(65535, 0, 0)); + break; + case uiDrawUnderlineColorGrammar: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 65535, 0)); + break; + case uiDrawUnderlineColorAuxiliary: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 0, 65535)); + break; + } + break; + // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt + case uiAttributeLanguage: + lang = pango_language_from_string((const char *) (spec->Value)); + addattr(p, start, end, + pango_attr_language_new(lang)); + // lang *cannot* be freed + break; + // TODO + default: + // handle typographic features + op.p = p; + op.start = start; + op.end = end; + // TODO check if unhandled and complain + specToOpenType(spec, doOpenType, &op); + break; + } + return 0; +} + +static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) +{ + struct foreachParams *p = (struct foreachParams *) data; + size_t *pos = (size_t *) key; + GString *s = (GString *) value; + + // remove the trailing comma/space + g_string_truncate(s, s->len - 2); + addattr(p, *pos, 1, + FUTURE_pango_attr_font_features_new(s->str)); + return TRUE; // always delete; we're emptying the map +} + +static void applyAndFreeFeatureAttributes(struct foreachParams *p) +{ + g_hash_table_foreach_remove(p->features, applyFeatures, p); + g_hash_table_destroy(p->features); +} + +PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/) +{ + struct foreachParams fep; + + fep.attrs = pango_attr_list_new(); + fep.features = g_hash_table_new_full( + featurePosHash, featurePosEqual, + g_free, freeFeatureString); + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeFeatureAttributes(&fep); + return fep.attrs; +} diff --git a/unix/future.c b/unix/future.c index 1f9f532b..3d63e9e0 100644 --- a/unix/future.c +++ b/unix/future.c @@ -6,6 +6,7 @@ // in others, because parts of GTK+ being unstable until recently also sucks :/ // added in pango 1.38; we need 1.36 +static PangoAttribute *(*newFeaturesAttr)(const gchar *features) = NULL; static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL; // added in GTK+ 3.20; we need 3.10 @@ -21,11 +22,19 @@ void loadFutures(void) if (handle == NULL) return; #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) + GET(newFeaturesAttr, pango_attr_font_features_new); GET(newFGAlphaAttr, pango_attr_foreground_alpha_new); GET(gwpIterSetObjectName, gtk_widget_path_iter_set_object_name); dlclose(handle); } +PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features) +{ + if (newFeaturesAttr == NULL) + return NULL; + return (*newFeaturesAttr)(features); +} + PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha) { if (newFGAlphaAttr == NULL) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index aaae8b34..d2ef5bf0 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "../ui.h" #include "../ui_unix.h" #include "../common/uipriv.h" @@ -54,8 +55,12 @@ extern GtkCellRenderer *newCellRendererButton(void); // future.c extern void loadFutures(void); +extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); // drawtext.c extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); + +// attrstr.c +extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/); From d4b38cd3b7f68fceb32d4d633faea9219ae4cfef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 19:28:19 -0500 Subject: [PATCH 183/487] And switched attributes on on GTK+. We have a problem with attributes that span bytes. --- unix/attrstr.c | 2 +- unix/drawtext.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 12cbb12d..e17ea75d 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -229,7 +229,7 @@ static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) // remove the trailing comma/space g_string_truncate(s, s->len - 2); - addattr(p, *pos, 1, + addattr(p, *pos, *pos + 1, FUTURE_pango_attr_font_features_new(s->str)); return TRUE; // always delete; we're emptying the map } diff --git a/unix/drawtext.c b/unix/drawtext.c index abc9aa65..16ede458 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -101,6 +101,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) uiDrawTextLayout *tl; PangoContext *context; PangoFontDescription *desc; + PangoAttrList *attrs; int pangoWidth; tl = uiNew(uiDrawTextLayout); @@ -137,7 +138,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - // TODO attributes + attrs = attrstrToPangoAttrList(p); + pango_layout_set_attributes(tl->layout, attrs); + pango_attr_list_unref(attrs); tl->nLines = pango_layout_get_line_count(tl->layout); computeLineMetrics(tl); From 4a1642cea241cac2c15ebfba3cf4353a2b39032d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 19:51:00 -0500 Subject: [PATCH 184/487] Fixed improper breaks in Unix attribute handling. --- darwin/attrstr.m | 1 + unix/attrstr.c | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c7e227cf..154adbe4 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -307,6 +307,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) fp = (struct fontParams *) [val pointerValue]; font = fontdescToCTFont(fp); range.location = [key integerValue]; + // TODO this is wrong for surrogate pairs range.length = 1; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); diff --git a/unix/attrstr.c b/unix/attrstr.c index e17ea75d..f627d6c1 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -1,9 +1,13 @@ // 12 february 2017 #include "uipriv_unix.h" +// TODO ligatures items turn ligatures *OFF*?! +// TODO actually does not specifying a feature revert to default? + // we need to collect all the OpenType features and background blocks and add them all at once // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { + const char *s; PangoAttrList *attrs; // keys are pointers to size_t maintained by g_new0()/g_free() // values are GStrings @@ -31,6 +35,8 @@ static void freeFeatureString(gpointer s) g_string_free((GString *) s, TRUE); } +#define isCodepointStart(b) (((uint8_t) (b)) <= 0x7F || ((uint8_t) (b)) >= 0xC0) + static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; @@ -38,6 +44,9 @@ static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t GString *new; for (i = start; i < end; i++) { + // don't create redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; new = (GString *) g_hash_table_lookup(p->features, &i); if (new != NULL) continue; @@ -79,6 +88,9 @@ static void doOpenType(const char *featureTag, uint32_t param, void *data) ensureFeaturesInRange(p->p, p->start, p->end); for (i = p->start; i < p->end; i++) { + // don't use redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; s = (GString *) g_hash_table_lookup(p->p->features, &i); g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", featureTag, param); @@ -226,10 +238,16 @@ static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) struct foreachParams *p = (struct foreachParams *) data; size_t *pos = (size_t *) key; GString *s = (GString *) value; + size_t n; // remove the trailing comma/space g_string_truncate(s, s->len - 2); - addattr(p, *pos, *pos + 1, + // make sure we cover an entire code point + // otherwise Pango will break apart multi-byte characters, spitting out U+FFFD characters at the invalid points + n = 1; + while (!isCodepointStart(p->s[*pos + n])) + n++; + addattr(p, *pos, *pos + n, FUTURE_pango_attr_font_features_new(s->str)); return TRUE; // always delete; we're emptying the map } @@ -244,6 +262,7 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray * { struct foreachParams fep; + fep.s = uiAttributedStringString(p->String); fep.attrs = pango_attr_list_new(); fep.features = g_hash_table_new_full( featurePosHash, featurePosEqual, From 4ba4e4ba23a162ceac308a5ca1d4c3736701b1b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 20:41:14 -0500 Subject: [PATCH 185/487] More attribute implementation. --- unix/attrstr.c | 27 +++++++++------------------ unix/drawtext.c | 6 +++--- unix/uipriv_unix.h | 6 ++++++ 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index f627d6c1..cdc72da5 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -1,10 +1,8 @@ // 12 february 2017 #include "uipriv_unix.h" -// TODO ligatures items turn ligatures *OFF*?! -// TODO actually does not specifying a feature revert to default? - // we need to collect all the OpenType features and background blocks and add them all at once +// TODO this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const char *s; @@ -89,7 +87,7 @@ static void doOpenType(const char *featureTag, uint32_t param, void *data) ensureFeaturesInRange(p->p, p->start, p->end); for (i = p->start; i < p->end; i++) { // don't use redundant entries; see below - if (!isCodepointStart(p->s[i])) + if (!isCodepointStart(p->p->s[i])) continue; s = (GString *) g_hash_table_lookup(p->p->features, &i); g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", @@ -120,30 +118,23 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t addattr(p, start, end, pango_attr_family_new((const char *) (spec->Value))); break; -#if 0 /* TODO */ case uiAttributeSize: addattr(p, start, end, pango_attr_size_new(cairoToPango(spec->Double))); break; case uiAttributeWeight: - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Weight = (uiDrawTextWeight) (spec->Value); - }); + // TODO reverse the misalignment from drawtext.c if it is corrected + addattr(p, start, end, + pango_attr_weight_new((PangoWeight) (spec->Value))); break; case uiAttributeItalic: - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Italic = (uiDrawTextItalic) (spec->Value); - }); + addattr(p, start, end, + pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); break; case uiAttributeStretch: - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); - }); + addattr(p, start, end, + pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); break; -#endif case uiAttributeColor: addattr(p, start, end, pango_attr_foreground_new( diff --git a/unix/drawtext.c b/unix/drawtext.c index 16ede458..f0e86b86 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -14,7 +14,7 @@ struct uiDrawTextLayout { // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description // For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double #define pangoToCairo(pango) (pango_units_to_double(pango)) -#define cairoToPango(cairo) (pango_units_from_double(cairo)) +// cairoToPango() is in uipriv_unix.h because attrstr.c needs it // we need a context for a few things // the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent @@ -22,13 +22,13 @@ struct uiDrawTextLayout { // so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us #define mkGenericPangoCairoContext() (gdk_pango_context_get()) -static const PangoStyle pangoItalics[] = { +const PangoStyle pangoItalics[] = { [uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL, [uiDrawTextItalicOblique] = PANGO_STYLE_OBLIQUE, [uiDrawTextItalicItalic] = PANGO_STYLE_ITALIC, }; -static const PangoStretch pangoStretches[] = { +const PangoStretch pangoStretches[] = { [uiDrawTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, [uiDrawTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, [uiDrawTextStretchCondensed] = PANGO_STRETCH_CONDENSED, diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index d2ef5bf0..78e94ad8 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -64,3 +64,9 @@ extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDraw // attrstr.c extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/); + +// drawtext.c +// TODO get rid of these (for attrstr.c) +#define cairoToPango(cairo) (pango_units_from_double(cairo)) +extern const PangoStyle pangoItalics[]; +extern const PangoStretch pangoStretches[]; From 75c2c805343ebdd0b38dac25c1791a80d053fbf9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 22:24:02 -0500 Subject: [PATCH 186/487] And adding the background attributes. We're done with the Pango one! Now for the Windows one... yay. --- unix/attrstr.c | 101 +++++++++++++++++++++++++++++++++++---------- unix/drawtext.c | 13 +++++- unix/uipriv_unix.h | 3 +- 3 files changed, 94 insertions(+), 23 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index cdc72da5..115b611e 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -10,7 +10,8 @@ struct foreachParams { // keys are pointers to size_t maintained by g_new0()/g_free() // values are GStrings GHashTable *features; -//TODO GArray *backgroundBlocks; + // TODO use pango's built-in background attribute? + GPtrArray *backgroundClosures; }; static gboolean featurePosEqual(gconstpointer a, gconstpointer b) @@ -55,21 +56,51 @@ static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t } } -#if 0 /* TODO */ -static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) -{ - return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { - uiDrawBrush brush; +struct closureParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; - brush.Type = uiDrawBrushTypeSolid; - brush.R = r; - brush.G = g; - brush.B = b; - brush.A = a; - drawTextBackground(c, x, y, layout, start, end, &brush, 0); - }); +static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +{ + struct closureParams *p = (struct closureParams *) data; + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = p->r; + brush.G = p->g; + brush.B = p->b; + brush.A = p->a; + drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); +} + +static void freeClosureParams(gpointer data, GClosure *closure) +{ + uiFree((struct closureParams *) data); +} + +static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) +{ + struct closureParams *p; + GClosure *closure; + + p = uiNew(struct closureParams); + p->start = start; + p->end = end; + p->r = r; + p->g = g; + p->b = b; + p->a = a; + closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); + // TODO write a specific marshaler + // TODO or drop the closure stuff entirely + g_closure_set_marshal(closure, g_cclosure_marshal_generic); + return closure; } -#endif struct otParam { struct foreachParams *p; @@ -107,7 +138,7 @@ static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttr static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; -//TODO backgroundBlock block; + GClosure *closure; PangoGravity gravity; PangoUnderline underline; PangoLanguage *lang; @@ -145,14 +176,11 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t FUTURE_pango_attr_foreground_alpha_new( (guint16) (spec->A * 65535.0))); break; -#if 0 /* TODO */ case uiAttributeBackground: - block = mkBackgroundBlock(ostart, oend, + closure = mkBackgroundClosure(start, end, spec->R, spec->G, spec->B, spec->A); - [p->backgroundBlocks addObject:block]; - Block_release(block); + g_ptr_array_add(p->backgroundClosures, closure); break; -#endif case uiAttributeVerticalForms: gravity = PANGO_GRAVITY_SOUTH; if (spec->Value != 0) @@ -249,7 +277,12 @@ static void applyAndFreeFeatureAttributes(struct foreachParams *p) g_hash_table_destroy(p->features); } -PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/) +static void unrefClosure(gpointer data) +{ + g_closure_unref((GClosure *) data); +} + +PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures) { struct foreachParams fep; @@ -258,7 +291,33 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray * fep.features = g_hash_table_new_full( featurePosHash, featurePosEqual, g_free, freeFeatureString); + fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFeatureAttributes(&fep); + *backgroundClosures = fep.backgroundClosures; return fep.attrs; } + +void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) +{ + GValue values[4] = { + // the zero-initialization is needed for g_value_init() to work + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + }; + + g_value_init(values + 0, G_TYPE_POINTER); + g_value_set_pointer(values + 0, c); + g_value_init(values + 1, G_TYPE_POINTER); + g_value_set_pointer(values + 1, layout); + g_value_init(values + 2, G_TYPE_DOUBLE); + g_value_set_double(values + 2, x); + g_value_init(values + 3, G_TYPE_DOUBLE); + g_value_set_double(values + 3, y); + g_closure_invoke(closure, + NULL, + 4, values, + NULL); +} diff --git a/unix/drawtext.c b/unix/drawtext.c index f0e86b86..8b45530a 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -7,6 +7,7 @@ struct uiDrawTextLayout { PangoLayout *layout; + GPtrArray *backgroundClosures; uiDrawTextLayoutLineMetrics *lineMetrics; int nLines; }; @@ -138,7 +139,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - attrs = attrstrToPangoAttrList(p); + attrs = attrstrToPangoAttrList(p, &(tl->backgroundClosures)); pango_layout_set_attributes(tl->layout, attrs); pango_attr_list_unref(attrs); @@ -151,12 +152,22 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->lineMetrics); + g_ptr_array_unref(tl->backgroundClosures); g_object_unref(tl->layout); uiFree(tl); } void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { + guint i; + GClosure *closure; + + for (i = 0; i < tl->backgroundClosures->len; i++) { + closure = (GClosure *) g_ptr_array_index(tl->backgroundClosures, i); + invokeBackgroundClosure(closure, c, tl, x, y); + } + // TODO have an implicit save/restore on each drawing functions instead? and is this correct? + cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); cairo_move_to(c->cr, x, y); pango_cairo_show_layout(c->cr, tl->layout); } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 78e94ad8..bde6502f 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -63,7 +63,8 @@ extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); // attrstr.c -extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/); +extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures); +extern void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); // drawtext.c // TODO get rid of these (for attrstr.c) From c111239b0d9a63154a81da6c53c6e23d67ccf145 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 22:25:49 -0500 Subject: [PATCH 187/487] More TODOs. --- unix/attrstr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unix/attrstr.c b/unix/attrstr.c index 115b611e..8882f3ac 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -1,6 +1,8 @@ // 12 february 2017 #include "uipriv_unix.h" +// TODO pango alpha attributes turn 0 into 65535 :| + // we need to collect all the OpenType features and background blocks and add them all at once // TODO this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO rename this struct to something that isn't exclusively foreach-ing? From efb7d5b21dc10b52795d1a2f9b971bd6a060506f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Feb 2017 16:06:29 -0500 Subject: [PATCH 188/487] More TODOs. --- windows/spinbox.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index 2b6af66d..8c4b666a 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -32,6 +32,7 @@ static int value(uiSpinbox *s) // control implementation +// TODO assign lResult static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) { uiSpinbox *s = (uiSpinbox *) c; From 9cc8b03516bc6da537d8fb60fdec0ec1f48af7d6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 03:43:43 -0500 Subject: [PATCH 189/487] Switched to a custom IDWriteTextRenderer, which will be necessary for some of our text attributes. More TODO. --- CMakeLists.txt | 3 +- examples/drawtext/hittest.c | 5 +- windows/drawtext.cpp | 142 +++++++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4a01049..f189e2b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,8 +202,9 @@ if(BUILD_SHARED_LIBS) endif() macro(_add_exec _name) + # TODOTODO re-add WIN32 when merging back add_executable(${_name} - WIN32 EXCLUDE_FROM_ALL + EXCLUDE_FROM_ALL ${ARGN}) target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES}) _target_link_options_private(${_name} diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 48d2c409..f5252388 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -1,9 +1,10 @@ // 20 january 2017 #include "drawtext.h" -// TODO double-check ligatures +// TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place // TODO the hiding and showing does not work properly on GTK+ -// TODO using the arrow keys allows us to walk back to the end of the line; IIRC arrow keys shouldn't do that +// TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that +// TODO make sure to check the cursor positions of RTL on all platforms static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index da512fd9..26b6a6ab 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -7,6 +7,8 @@ // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... // - what happens if any nLines == 0? +// TODO verify our renderer is correct, especially with regards to snapping + struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; @@ -240,23 +242,161 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl return brush; } +// some of the stuff we want to do isn't possible with what DirectWrite provides itself; we need to do it ourselves +// this is based on http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html +class textRenderer : public IDWriteTextRenderer { + ULONG refcount; + ID2D1RenderTarget *rt; + BOOL snap; + IUnknown *black; +public: + textRenderer(ID2D1RenderTarget *rt, BOOL snap, IUnknown *black) + { + this->refcount = 1; + this->rt = rt; + this->snap = snap; + this->black = black; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown || + riid == __uuidof (IDWritePixelSnapping) || + riid == __uuidof (IDWriteTextRenderer)) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + // IDWritePixelSnapping + virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(void *clientDrawingContext, DWRITE_MATRIX *transform) + { + D2D1_MATRIX_3X2_F d2dtf; + + if (transform == NULL) + return E_POINTER; + this->rt->GetTransform(&d2dtf); + transform->m11 = d2dtf._11; + transform->m12 = d2dtf._12; + transform->m21 = d2dtf._21; + transform->m22 = d2dtf._22; + transform->dx = d2dtf._31; + transform->dy = d2dtf._32; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void *clientDrawingContext, FLOAT *pixelsPerDip) + { + FLOAT dpix, dpiy; + + if (pixelsPerDip == NULL) + return E_POINTER; + this->rt->GetDpi(&dpix, &dpiy); + *pixelsPerDip = dpix / 96; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void *clientDrawingContext, BOOL *isDisabled) + { + if (isDisabled == NULL) + return E_POINTER; + *isDisabled = !this->snap; + return S_OK; + } + + // IDWriteTextRenderer + virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) + { + D2D1_POINT_2F baseline; + + baseline.x = baselineOriginX; + baseline.y = baselineOriginY; + if (clientDrawingEffect == NULL) + clientDrawingEffect = black; + this->rt->DrawGlyphRun( + baseline, + glyphRun, + (ID2D1Brush *) clientDrawingEffect, + measuringMode); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(void *clientDrawingContext, FLOAT originX, FLOAT originY, IDWriteInlineObject *inlineObject, BOOL isSideways, BOOL isRightToLeft, IUnknown *clientDrawingEffect) + { + if (inlineObject == NULL) + return E_POINTER; + return inlineObject->Draw(clientDrawingContext, this, + originX, originY, + isSideways, isRightToLeft, + clientDrawingEffect); + } + + virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) + { + // TODO + return S_OK; + } + + virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) + { + // TODO + return S_OK; + } +}; + // TODO this ignores clipping? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; ID2D1Brush *black; + textRenderer *renderer; + HRESULT hr; // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); +#if 0 pt.x = x; pt.y = y; // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP? - // TODO when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? + // TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? // TODO what is our pixel snapping setting related to the OPTIONS enum values? c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); +#else + renderer = new textRenderer(c->rt, + TRUE, // TODO FALSE for no-snap? + black); + hr = tl->layout->Draw(NULL, + renderer, + x, y); + if (hr != S_OK) + logHRESULT(L"error drawing IDWriteTextLayout", hr); + renderer->Release(); +#endif black->Release(); } From ae8105c234c6675c02cf6185108de90595080285 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 11:23:26 -0500 Subject: [PATCH 190/487] Added debugging to our custom IDWriteTextRenderer. --- windows/drawtext.cpp | 13 +++++++++++-- windows/fontdialog.cpp | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 26b6a6ab..91c5a0c4 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -378,7 +378,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO figure out if this needs to be cleaned out black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); -#if 0 +#define renderD2D 0 +#define renderOur 1 +#if renderD2D pt.x = x; pt.y = y; // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? @@ -386,7 +388,14 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? // TODO what is our pixel snapping setting related to the OPTIONS enum values? c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); -#else +#endif +#if renderD2D && renderOur + // draw ours semitransparent so we can check + // TODO get the actual color Charles Petzold uses and use that + black->Release(); + black = mkSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); +#endif +#if renderOur renderer = new textRenderer(c->rt, TRUE, // TODO FALSE for no-snap? black); diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index d7fa91e9..6096d442 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -6,6 +6,8 @@ // - the Choose Font sample defaults to Regular/Italic/Bold/Bold Italic in some case (no styles?); do we? find out what the case is // - do we set initial family and style topmost as well? // - this should probably just handle IDWriteFonts +// - localization? +// - the Sample window overlaps the groupbox in a weird way (compare to the real ChooseFont() dialog) struct fontDialog { HWND hwnd; From fb04feaebb828e1520eeb2e6389f787d98c5f9a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 12:49:55 -0500 Subject: [PATCH 191/487] Started Windows attribute handling. --- windows/CMakeLists.txt | 1 + windows/attrstr.cpp | 318 +++++++++++++++++++++++++++++++++++++++++ windows/draw.hpp | 3 + windows/drawtext.cpp | 2 + 4 files changed, 324 insertions(+) create mode 100644 windows/attrstr.cpp diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 5c401d06..f4a10998 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND _LIBUI_SOURCES windows/areaevents.cpp windows/areascroll.cpp windows/areautil.cpp + windows/attrstr.cpp windows/box.cpp windows/button.cpp windows/checkbox.cpp diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp new file mode 100644 index 00000000..10e9002a --- /dev/null +++ b/windows/attrstr.cpp @@ -0,0 +1,318 @@ +// 12 february 2017 +#include "uipriv_windows.hpp" +#include "draw.hpp" + +// we need to collect all the OpenType features and background blocks and add them all at once +// TODO(TODO does not seem to apply here) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly +// TODO contextual alternates override ligatures? +// TODO rename this struct to something that isn't exclusively foreach-ing? +struct foreachParams { + const uint16_t *s; + IDWriteTextLayout *layout; + std::map *features; +//TODO GPtrArray *backgroundClosures; +}; + +#define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) + +static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +{ + size_t i; + size_t *key; + IDWriteTypography *t; + HRESULT hr; + + for (i = start; i < end; i++) { + // don't create redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; + t = (*(p->features))[i]; + if (t != NULL) + continue; + hr = dwfactory->CreateTypography(&t); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTypography", hr); + (*(p->features))[i] = t; + } +} + +#if 0 /* TODO */ +struct closureParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; + +static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +{ + struct closureParams *p = (struct closureParams *) data; + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = p->r; + brush.G = p->g; + brush.B = p->b; + brush.A = p->a; + drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); +} + +static void freeClosureParams(gpointer data, GClosure *closure) +{ + uiFree((struct closureParams *) data); +} + +static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) +{ + struct closureParams *p; + GClosure *closure; + + p = uiNew(struct closureParams); + p->start = start; + p->end = end; + p->r = r; + p->g = g; + p->b = b; + p->a = a; + closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); + // TODO write a specific marshaler + // TODO or drop the closure stuff entirely + g_closure_set_marshal(closure, g_cclosure_marshal_generic); + return closure; +} +#endif + +struct otParam { + struct foreachParams *p; + size_t start; + size_t end; +}; + +static void doOpenType(const char *featureTag, uint32_t param, void *data) +{ + struct otParam *p = (struct otParam *) data; + size_t i; + IDWriteTypography *t; + DWRITE_FONT_FEATURE feature; + HRESULT hr; + + feature.nameTag = (DWRITE_FONT_FEATURE_TAG) DWRITE_MAKE_OPENTYPE_TAG( + featureTag[0], + featureTag[1], + featureTag[2], + featureTag[3]); + feature.parameter = param; + ensureFeaturesInRange(p->p, p->start, p->end); + for (i = p->start; i < p->end; i++) { + // don't use redundant entries; see below + if (!isCodepointStart(p->p->s[i])) + continue; + t = (*(p->p->features))[i]; + hr = t->AddFontFeature(feature); + if (hr != S_OK) + logHRESULT(L"error adding feature to IDWriteTypography", hr); + } +} + +static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; + DWRITE_TEXT_RANGE range; + WCHAR *wfamily; +#if 0 /* TODO */ + GClosure *closure; + PangoGravity gravity; + PangoUnderline underline; + PangoLanguage *lang; +#endif + struct otParam op; + HRESULT hr; + + start = attrstrUTF8ToUTF16(s, start); + end = attrstrUTF8ToUTF16(s, end); + range.startPosition = start; + range.length = end - start; + switch (spec->Type) { + case uiAttributeFamily: + wfamily = toUTF16((char *) (spec->Value)); + hr = p->layout->SetFontFamilyName(wfamily, range); + if (hr != S_OK) + logHRESULT(L"error applying family name attribute", hr); + uiFree(wfamily); + break; + case uiAttributeSize: + hr = p->layout->SetFontSize( +// TODO unify with drawtext.cpp +#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) + pointSizeToDWriteSize(spec->Double), + range); + if (hr != S_OK) + logHRESULT(L"error applying size attribute", hr); + break; +#if 0 /* TODO */ + case uiAttributeWeight: + // TODO reverse the misalignment from drawtext.c if it is corrected + addattr(p, start, end, + pango_attr_weight_new((PangoWeight) (spec->Value))); + break; + case uiAttributeItalic: + addattr(p, start, end, + pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); + break; + case uiAttributeStretch: + addattr(p, start, end, + pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); + break; + case uiAttributeColor: + addattr(p, start, end, + pango_attr_foreground_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_foreground_alpha_new( + (guint16) (spec->A * 65535.0))); + break; + case uiAttributeBackground: + closure = mkBackgroundClosure(start, end, + spec->R, spec->G, spec->B, spec->A); + g_ptr_array_add(p->backgroundClosures, closure); + break; + case uiAttributeVerticalForms: + gravity = PANGO_GRAVITY_SOUTH; + if (spec->Value != 0) + gravity = PANGO_GRAVITY_EAST; + addattr(p, start, end, + pango_attr_gravity_new(gravity)); + break; + case uiAttributeUnderline: + switch (spec->Value) { + case uiDrawUnderlineStyleNone: + underline = PANGO_UNDERLINE_NONE; + break; + case uiDrawUnderlineStyleSingle: + underline = PANGO_UNDERLINE_SINGLE; + break; + case uiDrawUnderlineStyleDouble: + underline = PANGO_UNDERLINE_DOUBLE; + break; + case uiDrawUnderlineStyleSuggestion: + underline = PANGO_UNDERLINE_ERROR; + break; + } + addattr(p, start, end, + pango_attr_underline_new(underline)); + break; + case uiAttributeUnderlineColor: + switch (spec->Value) { + case uiDrawUnderlineColorCustom: + addattr(p, start, end, + pango_attr_underline_color_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + break; + case uiDrawUnderlineColorSpelling: + // TODO GtkTextView style property error-underline-color + addattr(p, start, end, + pango_attr_underline_color_new(65535, 0, 0)); + break; + case uiDrawUnderlineColorGrammar: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 65535, 0)); + break; + case uiDrawUnderlineColorAuxiliary: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 0, 65535)); + break; + } + break; + // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt + case uiAttributeLanguage: + lang = pango_language_from_string((const char *) (spec->Value)); + addattr(p, start, end, + pango_attr_language_new(lang)); + // lang *cannot* be freed + break; +#endif + // TODO + default: + // handle typographic features + op.p = p; + op.start = start; + op.end = end; + // TODO check if unhandled and complain + specToOpenType(spec, doOpenType, &op); + break; + } + return 0; +} + +static void applyAndFreeFeatureAttributes(struct foreachParams *p) +{ + DWRITE_TEXT_RANGE range; + HRESULT hr; + + for (auto iter = p->features->begin(); iter != p->features->end(); iter++) { + // make sure we cover an entire code point + range.startPosition = iter->first; + range.length = 1; + if (!isCodepointStart(p->s[iter->first])) + range.length = 2; + hr = p->layout->SetTypography(iter->second, range); + if (hr != S_OK) + logHRESULT(L"error applying typographic features", hr); + iter->second->Release(); + } + delete p->features; +} + +#if 0 /* TODO */ +static void unrefClosure(gpointer data) +{ + g_closure_unref((GClosure *) data); +} +#endif + +void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/) +{ + struct foreachParams fep; + + fep.s = attrstrUTF16(p->String); + fep.layout = layout; + fep.features = new std::map; +//TODO fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeFeatureAttributes(&fep); +//TODO *backgroundClosures = fep.backgroundClosures; +} + +#if 0 +void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) +{ + GValue values[4] = { + // the zero-initialization is needed for g_value_init() to work + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + }; + + g_value_init(values + 0, G_TYPE_POINTER); + g_value_set_pointer(values + 0, c); + g_value_init(values + 1, G_TYPE_POINTER); + g_value_set_pointer(values + 1, layout); + g_value_init(values + 2, G_TYPE_DOUBLE); + g_value_set_double(values + 2, x); + g_value_init(values + 3, G_TYPE_DOUBLE); + g_value_set_double(values + 3, y); + g_closure_invoke(closure, + NULL, + 4, values, + NULL); +} +#endif diff --git a/windows/draw.hpp b/windows/draw.hpp index b015791f..ce177523 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -14,3 +14,6 @@ extern ID2D1PathGeometry *pathGeometry(uiDrawPath *p); // drawmatrix.cpp extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); + +// attrstr.cpp +extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/); diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 91c5a0c4..9f321f12 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -196,6 +196,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); + attrstrToIDWriteTextLayoutAttrs(p, tl->layout); + computeLineInfo(tl); // and finally copy the UTF-8/UTF-16 index conversion tables From b42250e3a9e344de9e1a86873e462a684be320d8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 15:19:11 -0500 Subject: [PATCH 192/487] More text attributes on Windows, including the beginning of drawing effects for colors and underlines. --- windows/attrstr.cpp | 106 ++++++++++++++++++++++++++++++++----------- windows/draw.hpp | 62 +++++++++++++++++++++++++ windows/drawtext.cpp | 50 ++++++++++++++------ windows/winapi.hpp | 1 + 4 files changed, 177 insertions(+), 42 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 10e9002a..31fefcae 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -2,19 +2,42 @@ #include "uipriv_windows.hpp" #include "draw.hpp" -// we need to collect all the OpenType features and background blocks and add them all at once +// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() +// we also need to collect all the OpenType features and background blocks and add them all at once // TODO(TODO does not seem to apply here) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; IDWriteTextLayout *layout; + std::map *effects; std::map *features; //TODO GPtrArray *backgroundClosures; }; #define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) +static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function f) +{ + size_t i; + size_t *key; + textDrawingEffect *t; + + for (i = start; i < end; i++) { + // don't create redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; + t = (*(p->effects))[i]; + if (t != NULL) { + f(t); + continue; + } + t = new textDrawingEffect; + f(t); + (*(p->effects))[i] = t; + } +} + static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; @@ -124,9 +147,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t #if 0 /* TODO */ GClosure *closure; PangoGravity gravity; - PangoUnderline underline; - PangoLanguage *lang; #endif + WCHAR *localeName; struct otParam op; HRESULT hr; @@ -151,30 +173,38 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (hr != S_OK) logHRESULT(L"error applying size attribute", hr); break; -#if 0 /* TODO */ case uiAttributeWeight: - // TODO reverse the misalignment from drawtext.c if it is corrected - addattr(p, start, end, - pango_attr_weight_new((PangoWeight) (spec->Value))); + // TODO reverse the misalignment from drawtext.cpp if it is corrected + hr = p->layout->SetFontWeight( + (DWRITE_FONT_WEIGHT) (spec->Value), + range); + if (hr != S_OK) + logHRESULT(L"error applying weight attribute", hr); break; case uiAttributeItalic: - addattr(p, start, end, - pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); + hr = p->layout->SetFontStyle( + dwriteItalics[(uiDrawTextItalic) (spec->Value)], + range); + if (hr != S_OK) + logHRESULT(L"error applying italic attribute", hr); break; case uiAttributeStretch: - addattr(p, start, end, - pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); + hr = p->layout->SetFontStretch( + dwriteStretches[(uiDrawTextStretch) (spec->Value)], + range); + if (hr != S_OK) + logHRESULT(L"error applying stretch attribute", hr); break; case uiAttributeColor: - addattr(p, start, end, - pango_attr_foreground_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); - addattr(p, start, end, - FUTURE_pango_attr_foreground_alpha_new( - (guint16) (spec->A * 65535.0))); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasColor = true; + t->r = spec->R; + t->g = spec->G; + t->b = spec->B; + t->a = spec->A; + }); break; +#if 0 case uiAttributeBackground: closure = mkBackgroundClosure(start, end, spec->R, spec->G, spec->B, spec->A); @@ -231,14 +261,15 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt - case uiAttributeLanguage: - lang = pango_language_from_string((const char *) (spec->Value)); - addattr(p, start, end, - pango_attr_language_new(lang)); - // lang *cannot* be freed - break; #endif + // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt + case uiAttributeLanguage: + localeName = toUTF16((char *) (spec->Value)); + hr = p->layout->SetLocaleName(localeName, range); + if (hr != S_OK) + logHRESULT(L"error applying locale name attribute", hr); + uiFree(localeName); + break; // TODO default: // handle typographic features @@ -252,6 +283,25 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } +static void applyAndFreeEffectsAttributes(struct foreachParams *p) +{ + DWRITE_TEXT_RANGE range; + HRESULT hr; + + for (auto iter = p->effects->begin(); iter != p->effects->end(); iter++) { + // make sure we cover an entire code point + range.startPosition = iter->first; + range.length = 1; + if (!isCodepointStart(p->s[iter->first])) + range.length = 2; + hr = p->layout->SetDrawingEffect(iter->second, range); + if (hr != S_OK) + logHRESULT(L"error applying drawing effects attributes", hr); + iter->second->Release(); + } + delete p->effects; +} + static void applyAndFreeFeatureAttributes(struct foreachParams *p) { DWRITE_TEXT_RANGE range; @@ -265,7 +315,7 @@ static void applyAndFreeFeatureAttributes(struct foreachParams *p) range.length = 2; hr = p->layout->SetTypography(iter->second, range); if (hr != S_OK) - logHRESULT(L"error applying typographic features", hr); + logHRESULT(L"error applying typographic features attributes", hr); iter->second->Release(); } delete p->features; @@ -284,9 +334,11 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou fep.s = attrstrUTF16(p->String); fep.layout = layout; + fep.effects = new std::map; fep.features = new std::map; //TODO fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeEffectsAttributes(&fep); applyAndFreeFeatureAttributes(&fep); //TODO *backgroundClosures = fep.backgroundClosures; } diff --git a/windows/draw.hpp b/windows/draw.hpp index ce177523..adc68294 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -17,3 +17,65 @@ extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); // attrstr.cpp extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/); + +// drawtext.cpp +class textDrawingEffect : public IUnknown { + ULONG refcount; +public: + bool hasColor; + double r; + double g; + double b; + double a; + + bool hasUnderline; + uiDrawUnderlineStyle u; + + bool hasUnderlineColor; + double ur; + double ug; + double ub; + double ua; + + textDrawingEffect() + { + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + +}; +// TODO these should not be exported +extern std::map dwriteItalics; +extern std::map dwriteStretches; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 9f321f12..31d6daee 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -28,14 +28,14 @@ struct uiDrawTextLayout { #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) // TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -static std::map dwriteItalics = { +std::map dwriteItalics = { { uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, { uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, { uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, }; // TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -static std::map dwriteStretches = { +std::map dwriteStretches = { { uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, { uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, { uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, @@ -219,12 +219,10 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } -static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush) { D2D1_BRUSH_PROPERTIES props; D2D1_COLOR_F color; - ID2D1SolidColorBrush *brush; - HRESULT hr; ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); props.opacity = 1.0; @@ -235,10 +233,18 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl color.g = g; color.b = b; color.a = a; - hr = rt->CreateSolidColorBrush( + return rt->CreateSolidColorBrush( &color, &props, - &brush); + brush); +} + +static ID2D1SolidColorBrush *mustMakeSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +{ + ID2D1SolidColorBrush *brush; + HRESULT hr; + + hr = mkSolidBrush(rt, r, g, b, a, &brush); if (hr != S_OK) logHRESULT(L"error creating solid brush", hr); return brush; @@ -250,9 +256,9 @@ class textRenderer : public IDWriteTextRenderer { ULONG refcount; ID2D1RenderTarget *rt; BOOL snap; - IUnknown *black; + ID2D1SolidColorBrush *black; public: - textRenderer(ID2D1RenderTarget *rt, BOOL snap, IUnknown *black) + textRenderer(ID2D1RenderTarget *rt, BOOL snap, ID2D1SolidColorBrush *black) { this->refcount = 1; this->rt = rt; @@ -332,16 +338,26 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) { D2D1_POINT_2F baseline; + textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + ID2D1SolidColorBrush *brush; baseline.x = baselineOriginX; baseline.y = baselineOriginY; - if (clientDrawingEffect == NULL) - clientDrawingEffect = black; + brush = this->black; + if (t != NULL && t->hasColor) { + HRESULT hr; + + hr = mkSolidBrush(this->rt, t->r, t->g, t->b, t->a, &brush); + if (hr != S_OK) + return hr; + } this->rt->DrawGlyphRun( baseline, glyphRun, - (ID2D1Brush *) clientDrawingEffect, + brush, measuringMode); + if (t != NULL && t->hasColor) + brush->Release(); return S_OK; } @@ -357,12 +373,16 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) { + textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + // TODO return S_OK; } virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { + textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + // TODO return S_OK; } @@ -372,13 +392,13 @@ public: void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; - ID2D1Brush *black; + ID2D1SolidColorBrush *black; textRenderer *renderer; HRESULT hr; // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out - black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); + black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); #define renderD2D 0 #define renderOur 1 @@ -395,7 +415,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // draw ours semitransparent so we can check // TODO get the actual color Charles Petzold uses and use that black->Release(); - black = mkSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); + black = mustMakeSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); #endif #if renderOur renderer = new textRenderer(c->rt, diff --git a/windows/winapi.hpp b/windows/winapi.hpp index a540809c..c5796faf 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -47,4 +47,5 @@ #include #include #include +#include #endif From f2b158b529e521ecac2bf0a4d233a6f44b839ae8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 19:13:36 -0500 Subject: [PATCH 193/487] And finished implementing attributes on Windows. --- examples/drawtext/attributes.c | 10 +- windows/attrstr.cpp | 192 ++++++++++++--------------------- windows/draw.hpp | 3 +- windows/drawtext.cpp | 120 +++++++++++++++++++-- 4 files changed, 194 insertions(+), 131 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index f0868a8b..24dd4a19 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -100,7 +100,15 @@ static void setupAttributedString(void) spec.Type = uiAttributeVerticalForms; spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); + uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, "); + next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 31fefcae..c6d366c5 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -12,7 +12,7 @@ struct foreachParams { IDWriteTextLayout *layout; std::map *effects; std::map *features; -//TODO GPtrArray *backgroundClosures; + std::vector *backgroundFuncs; }; #define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) @@ -59,54 +59,20 @@ static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t } } -#if 0 /* TODO */ -struct closureParams { - size_t start; - size_t end; - double r; - double g; - double b; - double a; -}; - -static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) { - struct closureParams *p = (struct closureParams *) data; - uiDrawBrush brush; + return [=](uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { + uiDrawBrush brush; - brush.Type = uiDrawBrushTypeSolid; - brush.R = p->r; - brush.G = p->g; - brush.B = p->b; - brush.A = p->a; - drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); + brush.Type = uiDrawBrushTypeSolid; + brush.R = r; + brush.G = g; + brush.B = b; + brush.A = a; + drawTextBackground(c, x, y, layout, start, end, &brush, 0); + }; } -static void freeClosureParams(gpointer data, GClosure *closure) -{ - uiFree((struct closureParams *) data); -} - -static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) -{ - struct closureParams *p; - GClosure *closure; - - p = uiNew(struct closureParams); - p->start = start; - p->end = end; - p->r = r; - p->g = g; - p->b = b; - p->a = a; - closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); - // TODO write a specific marshaler - // TODO or drop the closure stuff entirely - g_closure_set_marshal(closure, g_cclosure_marshal_generic); - return closure; -} -#endif - struct otParam { struct foreachParams *p; size_t start; @@ -144,10 +110,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; WCHAR *wfamily; -#if 0 /* TODO */ - GClosure *closure; - PangoGravity gravity; -#endif + BOOL hasUnderline; + uint32_t vertval; WCHAR *localeName; struct otParam op; HRESULT hr; @@ -204,64 +168,79 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t t->a = spec->A; }); break; -#if 0 case uiAttributeBackground: - closure = mkBackgroundClosure(start, end, - spec->R, spec->G, spec->B, spec->A); - g_ptr_array_add(p->backgroundClosures, closure); + p->backgroundFuncs->push_back( + mkBackgroundFunc(start, end, + spec->R, spec->G, spec->B, spec->A)); break; case uiAttributeVerticalForms: - gravity = PANGO_GRAVITY_SOUTH; + // LONGTERM 8 and/or 8.1 add other methods for vertical text + op.p = p; + op.start = start; + op.end = end; + vertval = 0; if (spec->Value != 0) - gravity = PANGO_GRAVITY_EAST; - addattr(p, start, end, - pango_attr_gravity_new(gravity)); + vertval = 1; + doOpenType("vert", vertval, &op); + doOpenType("vrt2", vertval, &op); + doOpenType("vkrn", vertval, &op); + doOpenType("vrtr", vertval, &op); break; case uiAttributeUnderline: - switch (spec->Value) { - case uiDrawUnderlineStyleNone: - underline = PANGO_UNDERLINE_NONE; - break; - case uiDrawUnderlineStyleSingle: - underline = PANGO_UNDERLINE_SINGLE; - break; - case uiDrawUnderlineStyleDouble: - underline = PANGO_UNDERLINE_DOUBLE; - break; - case uiDrawUnderlineStyleSuggestion: - underline = PANGO_UNDERLINE_ERROR; - break; - } - addattr(p, start, end, - pango_attr_underline_new(underline)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderline = true; + t->u = (uiDrawUnderlineStyle) (spec->Value); + }); + // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method + hasUnderline = FALSE; + if ((uiDrawUnderlineStyle) (spec->Value) != uiDrawUnderlineStyleNone) + hasUnderline = TRUE; + hr = p->layout->SetUnderline(hasUnderline, range); + if (hr != S_OK) + logHRESULT(L"error applying underline attribute", hr); break; case uiAttributeUnderlineColor: switch (spec->Value) { case uiDrawUnderlineColorCustom: - addattr(p, start, end, - pango_attr_underline_color_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = spec->R; + t->ug = spec->G; + t->ub = spec->B; + t->ua = spec->A; + }); break; + // TODO see if Microsoft has any standard colors for this case uiDrawUnderlineColorSpelling: // TODO GtkTextView style property error-underline-color - addattr(p, start, end, - pango_attr_underline_color_new(65535, 0, 0)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = 1.0; + t->ug = 0.0; + t->ub = 0.0; + t->ua = 1.0; + }); break; case uiDrawUnderlineColorGrammar: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 65535, 0)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = 0.0; + t->ug = 1.0; + t->ub = 0.0; + t->ua = 1.0; + }); break; case uiDrawUnderlineColorAuxiliary: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 0, 65535)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = 0.0; + t->ug = 0.0; + t->ub = 1.0; + t->ua = 1.0; + }); break; } break; -#endif // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt case uiAttributeLanguage: localeName = toUTF16((char *) (spec->Value)); @@ -321,14 +300,7 @@ static void applyAndFreeFeatureAttributes(struct foreachParams *p) delete p->features; } -#if 0 /* TODO */ -static void unrefClosure(gpointer data) -{ - g_closure_unref((GClosure *) data); -} -#endif - -void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/) +void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) { struct foreachParams fep; @@ -336,35 +308,9 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou fep.layout = layout; fep.effects = new std::map; fep.features = new std::map; -//TODO fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); + fep.backgroundFuncs = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeEffectsAttributes(&fep); applyAndFreeFeatureAttributes(&fep); -//TODO *backgroundClosures = fep.backgroundClosures; + *backgroundFuncs = fep.backgroundFuncs; } - -#if 0 -void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) -{ - GValue values[4] = { - // the zero-initialization is needed for g_value_init() to work - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - }; - - g_value_init(values + 0, G_TYPE_POINTER); - g_value_set_pointer(values + 0, c); - g_value_init(values + 1, G_TYPE_POINTER); - g_value_set_pointer(values + 1, layout); - g_value_init(values + 2, G_TYPE_DOUBLE); - g_value_set_double(values + 2, x); - g_value_init(values + 3, G_TYPE_DOUBLE); - g_value_set_double(values + 3, y); - g_closure_invoke(closure, - NULL, - 4, values, - NULL); -} -#endif diff --git a/windows/draw.hpp b/windows/draw.hpp index adc68294..f3a8d387 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -16,7 +16,8 @@ extern ID2D1PathGeometry *pathGeometry(uiDrawPath *p); extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); // attrstr.cpp -extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/); +typedef std::function backgroundFunc; +extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); // drawtext.cpp class textDrawingEffect : public IUnknown { diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 31d6daee..3510a379 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -6,12 +6,14 @@ // - consider the warnings about antialiasing in the PadWrite sample // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... // - what happens if any nLines == 0? +// - paragraph alignment is subject to RTL mirroring; see if it is on other platforms // TODO verify our renderer is correct, especially with regards to snapping struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; + std::vector *backgroundFuncs; UINT32 nLines; struct lineInfo *lineInfo; // for converting DirectWrite indices from/to byte offsets @@ -196,7 +198,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); - attrstrToIDWriteTextLayoutAttrs(p, tl->layout); + attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundFuncs)); computeLineInfo(tl); @@ -214,6 +216,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl->u16tou8); uiFree(tl->u8tou16); uiFree(tl->lineInfo); + delete tl->backgroundFuncs; tl->layout->Release(); tl->format->Release(); uiFree(tl); @@ -373,17 +376,118 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) { - textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; - - // TODO - return S_OK; + // we don't support strikethrough + return E_UNEXPECTED; } virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + ID2D1SolidColorBrush *brush; + D2D1_RECT_F rect; + D2D1::Matrix3x2F pixeltf; + FLOAT dpix, dpiy; + D2D1_POINT_2F pt; - // TODO + if (underline == NULL) + return E_POINTER; + if (t == NULL) // we can only get here through an underline + return E_UNEXPECTED; + brush = this->black; + if (t->hasUnderlineColor) { + HRESULT hr; + + hr = mkSolidBrush(this->rt, t->ur, t->ug, t->ub, t->ua, &brush); + if (hr != S_OK) + return hr; + } else if (t->hasColor) { + // TODO formalize this rule + HRESULT hr; + + hr = mkSolidBrush(this->rt, t->r, t->g, t->b, t->a, &brush); + if (hr != S_OK) + return hr; + } + rect.left = baselineOriginX; + rect.top = baselineOriginY + underline->offset; + rect.right = rect.left + underline->width; + rect.bottom = rect.top + underline->thickness; + switch (t->u) { + case uiDrawUnderlineStyleSingle: + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleDouble: + // TODO do any of the matrix methods return errors? + // TODO standardize double-underline shape across platforms? wavy underline shape? + this->rt->GetTransform(&pixeltf); + this->rt->GetDpi(&dpix, &dpiy); + pixeltf = pixeltf * D2D1::Matrix3x2F::Scale(dpix / 96, dpiy / 96); + pt.x = 0; + pt.y = rect.top; + pt = pixeltf.TransformPoint(pt); + rect.top = (FLOAT) ((int) (pt.y + 0.5)); + pixeltf.Invert(); + pt = pixeltf.TransformPoint(pt); + rect.top = pt.y; + // first line + rect.top -= underline->thickness; + // and it seems we need to recompute this + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + // second line + rect.top += 2 * underline->thickness; + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleSuggestion: + { // TODO get rid of the extra block + // TODO properly clean resources on failure + // TODO use fully qualified C overloads for all methods + // TODO ensure all methods properly have errors handled + ID2D1PathGeometry *path; + ID2D1GeometrySink *sink; + double amplitude, period, xOffset, yOffset; + double t; + bool first = true; + HRESULT hr; + + hr = d2dfactory->CreatePathGeometry(&path); + if (hr != S_OK) + return hr; + hr = path->Open(&sink); + if (hr != S_OK) + return hr; + amplitude = underline->thickness; + period = 5 * underline->thickness; + xOffset = baselineOriginX; + yOffset = baselineOriginY + underline->offset; + for (t = 0; t < underline->width; t++) { + double x, angle, y; + D2D1_POINT_2F pt; + + x = t + xOffset; + angle = 2 * uiPi * fmod(x, period) / period; + y = amplitude * sin(angle) + yOffset; + pt.x = x; + pt.y = y; + if (first) { + sink->BeginFigure(pt, D2D1_FIGURE_BEGIN_HOLLOW); + first = false; + } else + sink->AddLine(pt); + } + sink->EndFigure(D2D1_FIGURE_END_OPEN); + hr = sink->Close(); + if (hr != S_OK) + return hr; + sink->Release(); + this->rt->DrawGeometry(path, brush, underline->thickness); + path->Release(); + } + break; + } + if (t->hasUnderlineColor || t->hasColor) + brush->Release(); return S_OK; } }; @@ -396,6 +500,10 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) textRenderer *renderer; HRESULT hr; + // TODO the "any combination of the above" one isn't drawn in the right place but the "multiple backgrounds" one is (at least for when there's a line break; TODO) + for (const auto &f : *(tl->backgroundFuncs)) + f(c, tl, x, y); + // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); From 0f2b2b1fe5d427582f210f40a3e2140ab9dd2f70 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 21:46:15 -0500 Subject: [PATCH 194/487] Fixed background drawing on Windows. --- windows/attrstr.cpp | 5 ++++- windows/drawtext.cpp | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index c6d366c5..6c96a8a4 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -110,12 +110,15 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; WCHAR *wfamily; + size_t ostart, oend; BOOL hasUnderline; uint32_t vertval; WCHAR *localeName; struct otParam op; HRESULT hr; + ostart = start; + oend = end; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); range.startPosition = start; @@ -170,7 +173,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; case uiAttributeBackground: p->backgroundFuncs->push_back( - mkBackgroundFunc(start, end, + mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; case uiAttributeVerticalForms: diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 3510a379..67ec0a47 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -500,7 +500,6 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) textRenderer *renderer; HRESULT hr; - // TODO the "any combination of the above" one isn't drawn in the right place but the "multiple backgrounds" one is (at least for when there's a line break; TODO) for (const auto &f : *(tl->backgroundFuncs)) f(c, tl, x, y); From e5f3646fcf9605b6ac5fbdba1001e8981e00df39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 23 Feb 2017 21:37:41 -0500 Subject: [PATCH 195/487] Attempts to align the vertical glyphs with the orizontal baseline on OS X with Core Text. This is gonna be harder... --- darwin/attrstr.m | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 154adbe4..8b17da15 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -220,10 +220,23 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t Block_release(block); break; case uiAttributeVerticalForms: - if (spec->Value != 0) + if (spec->Value != 0) { CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); - else +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } else { CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } break; case uiAttributeUnderline: switch (spec->Value) { From be56ec3626a1937c74cb36f390023d4999611e6b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 01:23:47 -0500 Subject: [PATCH 196/487] Removed the vertical forms attribute. This is a compatiblity nightmare. --- _future/verticaltext/README | 13 +++++++++++++ _future/verticaltext/attrstr_darwin.m | 19 +++++++++++++++++++ _future/verticaltext/attrstr_unix.c | 9 +++++++++ _future/verticaltext/attrstr_windows.cpp | 15 +++++++++++++++ _future/verticaltext/common_attrlist.c | 2 ++ _future/verticaltext/drawtext_example.c | 18 ++++++++++++++++++ _future/verticaltext/ui.h | 2 ++ common/attrlist.c | 3 +-- darwin/attrstr.m | 19 ------------------- examples/drawtext/attributes.c | 19 ------------------- examples/drawtext/main.c | 12 ------------ ui_attrstr.h | 2 -- unix/attrstr.c | 8 -------- windows/attrstr.cpp | 14 -------------- 14 files changed, 79 insertions(+), 76 deletions(-) create mode 100644 _future/verticaltext/README create mode 100644 _future/verticaltext/attrstr_darwin.m create mode 100644 _future/verticaltext/attrstr_unix.c create mode 100644 _future/verticaltext/attrstr_windows.cpp create mode 100644 _future/verticaltext/common_attrlist.c create mode 100644 _future/verticaltext/drawtext_example.c create mode 100644 _future/verticaltext/ui.h diff --git a/_future/verticaltext/README b/_future/verticaltext/README new file mode 100644 index 00000000..d60f3629 --- /dev/null +++ b/_future/verticaltext/README @@ -0,0 +1,13 @@ +Proper vertical text support in uiDrawTextLayout was removed because DirectWrite doesn't add this until Windows 8.1 (unless I drop IDWriteTextLayout and do the script analysis myself; TODO consider this possibility). + +On OS X, setting the vertical forms attribute stacks non-vertical scripts in vertical text (rotates each individual glyph) with Core Text, whereas everything else — including Cocoa's text system — rotates entire non-vertical strings. Not sure what to do about this except manually detect which characters to apply the attribute to: +http://www.unicode.org/notes/tn22/RobustVerticalLayout.pdf +http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt + +In addition, with Core Text, the vertical forms attribute vertically centers the vertical glyphs on the bhorizontal baseline, rather than flush with the text. Using the baseline class attribute doesn't seem to work. + +If readded, this will need to be a layout-wide setting, not a per-character setting. Pango works right this way; the current Pango code doesn't seem to work. + +More links: +https://www.w3.org/TR/2012/NOTE-jlreq-20120403/#line-composition +https://www.w3.org/TR/REC-CSS2/notes.html diff --git a/_future/verticaltext/attrstr_darwin.m b/_future/verticaltext/attrstr_darwin.m new file mode 100644 index 00000000..f56adc7f --- /dev/null +++ b/_future/verticaltext/attrstr_darwin.m @@ -0,0 +1,19 @@ + case uiAttributeVerticalForms: + if (spec->Value != 0) { + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } else { + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } + break; diff --git a/_future/verticaltext/attrstr_unix.c b/_future/verticaltext/attrstr_unix.c new file mode 100644 index 00000000..e5e13617 --- /dev/null +++ b/_future/verticaltext/attrstr_unix.c @@ -0,0 +1,9 @@ + PangoGravity gravity; + + case uiAttributeVerticalForms: + gravity = PANGO_GRAVITY_SOUTH; + if (spec->Value != 0) + gravity = PANGO_GRAVITY_EAST; + addattr(p, start, end, + pango_attr_gravity_new(gravity)); + break; diff --git a/_future/verticaltext/attrstr_windows.cpp b/_future/verticaltext/attrstr_windows.cpp new file mode 100644 index 00000000..88369217 --- /dev/null +++ b/_future/verticaltext/attrstr_windows.cpp @@ -0,0 +1,15 @@ + uint32_t vertval; + + case uiAttributeVerticalForms: + // LONGTERM 8 and/or 8.1 add other methods for vertical text + op.p = p; + op.start = start; + op.end = end; + vertval = 0; + if (spec->Value != 0) + vertval = 1; + doOpenType("vert", vertval, &op); + doOpenType("vrt2", vertval, &op); + doOpenType("vkrn", vertval, &op); + doOpenType("vrtr", vertval, &op); + break; diff --git a/_future/verticaltext/common_attrlist.c b/_future/verticaltext/common_attrlist.c new file mode 100644 index 00000000..0b9ea921 --- /dev/null +++ b/_future/verticaltext/common_attrlist.c @@ -0,0 +1,2 @@ + case uiAttributeVerticalForms: + return boolsEqual(attr, spec); diff --git a/_future/verticaltext/drawtext_example.c b/_future/verticaltext/drawtext_example.c new file mode 100644 index 00000000..669bdaa8 --- /dev/null +++ b/_future/verticaltext/drawtext_example.c @@ -0,0 +1,18 @@ + next = "vertical glyph forms"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, "); + next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/_future/verticaltext/ui.h b/_future/verticaltext/ui.h new file mode 100644 index 00000000..98903ee9 --- /dev/null +++ b/_future/verticaltext/ui.h @@ -0,0 +1,2 @@ + // TODO rename to uiAttributeVertical? + uiAttributeVerticalForms, // 0 = off, 1 = on diff --git a/common/attrlist.c b/common/attrlist.c index 3bf8caff..20aaf97f 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -342,11 +342,10 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; - case uiAttributeVerticalForms: - return boolsEqual(attr, spec); case uiAttributeLanguage: return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); // TODO + // TODO use boolsEqual() on boolean features } // handles the rest return attr->spec.Value == spec->Value; diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 8b17da15..f4644b37 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -219,25 +219,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t [p->backgroundBlocks addObject:block]; Block_release(block); break; - case uiAttributeVerticalForms: - if (spec->Value != 0) { - CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); - } else { - CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); - } - break; case uiAttributeUnderline: switch (spec->Value) { case uiDrawUnderlineStyleNone: diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 24dd4a19..b4a697a8 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -93,25 +93,6 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "vertical glyph forms"; - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeVerticalForms; - spec.Value = 1; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, "); - next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"; - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeVerticalForms; - spec.Value = 1; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, ")"); - - uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "multiple"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 22b532fe..822382d0 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -1,18 +1,6 @@ // 17 january 2017 #include "drawtext.h" -// okay everything is definitely bugged in the OS X code -// - occasional segfaults on startup -// - very rare size attributes in the attributed string example don't terminate for a while, making everything big -// - very very rare trace/bpt faults on startup -/* -objc[14827]: autorelease pool page 0x7feeab88b000 corrupted - magic 0xe000007f 0xeea9f2df 0x0000007f 0xeea9f2e0 - should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341 - pthread 0x0 - should be 0x7fff727a1000 -*/ - static uiWindow *mainwin; static uiBox *box; static uiCombobox *exampleList; diff --git a/ui_attrstr.h b/ui_attrstr.h index 5e4fe8a8..7bc7cd1a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -10,8 +10,6 @@ _UI_ENUM(uiAttribute) { uiAttributeStretch, uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A - // TODO rename to uiAttributeVertical? - uiAttributeVerticalForms, // 0 = off, 1 = on // TODO kerning amount // OS X: kCTKernAttributeName diff --git a/unix/attrstr.c b/unix/attrstr.c index 8882f3ac..cf5bc509 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -141,7 +141,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t { struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; - PangoGravity gravity; PangoUnderline underline; PangoLanguage *lang; struct otParam op; @@ -183,13 +182,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t spec->R, spec->G, spec->B, spec->A); g_ptr_array_add(p->backgroundClosures, closure); break; - case uiAttributeVerticalForms: - gravity = PANGO_GRAVITY_SOUTH; - if (spec->Value != 0) - gravity = PANGO_GRAVITY_EAST; - addattr(p, start, end, - pango_attr_gravity_new(gravity)); - break; case uiAttributeUnderline: switch (spec->Value) { case uiDrawUnderlineStyleNone: diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 6c96a8a4..d0a4b3cc 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -112,7 +112,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; - uint32_t vertval; WCHAR *localeName; struct otParam op; HRESULT hr; @@ -176,19 +175,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; - case uiAttributeVerticalForms: - // LONGTERM 8 and/or 8.1 add other methods for vertical text - op.p = p; - op.start = start; - op.end = end; - vertval = 0; - if (spec->Value != 0) - vertval = 1; - doOpenType("vert", vertval, &op); - doOpenType("vrt2", vertval, &op); - doOpenType("vkrn", vertval, &op); - doOpenType("vrtr", vertval, &op); - break; case uiAttributeUnderline: ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { t->hasUnderline = true; From f65fc1f25eb1916d3003fde0c8cb702dc13e30d4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 10:29:08 -0500 Subject: [PATCH 197/487] Fixed text hit-testing on OS X. --- darwin/drawtext.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c5736bf9..c0123117 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -262,7 +262,8 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) - // TODO is x relative to the line origin? + // note: x is relative to the line origin + x -= tl->lineMetrics[*line].X; p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); if (p == kCFNotFound) { // TODO @@ -284,7 +285,9 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int if (pos < range.location || pos > (range.location + range.length)) return -1; // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ - return CTLineGetOffsetForStringIndex(lr, pos, NULL); + // note: the result is relative to the line origin (TODO find documentation to support this) + // TODO document that these functions do this + return CTLineGetOffsetForStringIndex(lr, pos, NULL) + tl->lineMetrics[line].X; } void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) From 074350bf99dec38ad2b2e36b3ed1596b5a88e043 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 11:49:32 -0500 Subject: [PATCH 198/487] Removed the TODOs from uiAttributes for attributes we don't yet support. That'll all come later. --- _future/otherattributes/ui.h | 190 +++++++++++++++++++++++++++++++++++ _future/verticaltext/README | 1 + ui_attrstr.h | 50 +-------- 3 files changed, 192 insertions(+), 49 deletions(-) create mode 100644 _future/otherattributes/ui.h diff --git a/_future/otherattributes/ui.h b/_future/otherattributes/ui.h new file mode 100644 index 00000000..89a0c5a1 --- /dev/null +++ b/_future/otherattributes/ui.h @@ -0,0 +1,190 @@ +_UI_ENUM(uiAttribute) { + uiAttributeFamily, + uiAttributeSize, // use Double + uiAttributeWeight, + uiAttributeItalic, + uiAttributeStretch, + uiAttributeColor, // use R, G, B, A + uiAttributeBackground, // use R, G, B, A + + // TODO kerning amount + // OS X: kCTKernAttributeName + // > 0: farther (TODO from advance or standard kerning?) + // == 0: no kerning + // < 0: closer (TODO same) + // undefined: standard kerning + // Pango: pango_attr_letter_spacing_new() + // parameter meaning unspecified + // Windows: requires Platform Update, SetLetterSpacing() + // parameter meaning unspecified + + uiAttributeUnderline, // enum uiDrawUnderlineStyle + // TODO what is the color in the case we don't specify it, black or the text color? + uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + + // TODO kCTSuperscriptAttributeName vs below + // all it does is set the below attribute so + + // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName + + // TODO strikethroughs? (pango yes, directwrite yes, os x no) + // TODO baseline offsets? (pango yes) + // TODO size scales? (pango yes) + // TODO fallbacks (pango: enable or disable) + + // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility + uiAttributeLanguage, // BCP 47 string + + // These attributes represent typographic features. Each feature + // is a separate attribute, to make composition easier. The + // availability of for each attribute are defined by the font; the + // default values are defined by the font and/or by the OS. + // + // A note about features whose parameter is an enumeration: + // OS X defines typographic features using the AAT specification + // and converts to OpenType internally when needed, whereas + // other platforms use OpenType directly. OpenType is less + // precise about what each enumeration value means than AAT + // is, so enumeration values do not necessarily represent what + // OS X expects with all fonts. In cases where they do, libui + // provides an enumeration type to use. Otherwise, the AAT + // enumeration values are provided in comments for + // documentation purposes. + + // TODO kAllTypographicFeaturesType + + // AAT calls these "common ligatures" + uiAttributeStandardLigatures, // 0 = off, 1 = on + uiAttributeRequiredLigatures, // 0 = off, 1 = on + // AAT calls these "rare ligatures" + uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on + uiAttributeContextualLigatures, // 0 = off, 1 = on + uiAttributeHistoricalLigatures, // 0 = off, 1 = on + + // TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all + + uiAttributeUnicase, // 0 = off, 1 = on + + // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on + + // TODO rename this + uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing + + // TODO kSmartSwashType, falt and jalt + + // TODO kDiacriticsType + + uiAttributeSuperscripts, // enum uiAttributeSuperscript + + uiAttributeFractionForms, // enum uiAttributeFractionForm + + uiAttributeSlashedZero, // 0 = off, 1 = on + + uiAttributeMathematicalGreek, // 0 = off, 1 = on + + // AAT defines the following values: + // 0 = none + // 1 = dingbats + // 2 = pi characters + // 3 = fleurons + // 4 = decorative borders + // 5 = international symbols + // 6 = mathematical symbols + // OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many + uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? + + // AAT calls this "character alternatives" and defines the + // following values: + // 0 = none + // OpenType calls this "access all alternates". + // TODO doesn't OpenType do the same about 0? + uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? + + uiAttributeTitlingCapitalForms, // 0 = off, 1 = on + + // AAT calls these "character shapes" + uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm + + // OpenType calls these "old-style" + uiAttributeLowercaseNumbers, // 0 = off, 1 = on + + // TODO kTextSpacingType + // see kKanaSpacingType below + + uiAttributeHanjaToHangul, // 0 = off, 1 = on + + // AAT defines the following values: + // 0 = none + // 1 = box + // 2 = rounded box + // 3 = circle + // 4 = inverted circle + // 5 = parentheses + // 6 = period + // 7 = roman numeral + // 8 = diamond + // 9 = inverted box + // 10 = inverted rounded box + // TODO rename to AnnotatedForms? + uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? + + // TODO kKanaSpacingType + // TODO kIdeographicSpacingType + // can they be provided independently of kTextSpacingType? Core Text doesn't seem to + + // TODO kUnicodeDecompositionType + + uiAttributeRubyKanaForms, // 0 = off, 1 = on + + // TODO kCJKVerticalRomanPlacementType + // this is 'valt' in OpenType but I don't know if I want to make it selectable or not + + uiAttributeCJKRomansToItalics, // 0 = off, 1 = on + + // AAT calls this "case-sensitive layout" + uiAttributeCaseSensitiveForms, // 0 = off, 1 = on + // AAT: this is called "case-sensitive spacing" + uiAttributeCapitalSpacing, // 0 = off, 1 = on + + uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on + uiAttributeAlternateVerticalKana, // 0 = off, 1 = on + + // TODO "Alternate"? unify all this + // TODO document that these are guaranteed to be consecutive + uiAttributeStylisticAlternative1, // 0 = off, 1 = on + uiAttributeStylisticAlternative2, // 0 = off, 1 = on + uiAttributeStylisticAlternative3, // 0 = off, 1 = on + uiAttributeStylisticAlternative4, // 0 = off, 1 = on + uiAttributeStylisticAlternative5, // 0 = off, 1 = on + uiAttributeStylisticAlternative6, // 0 = off, 1 = on + uiAttributeStylisticAlternative7, // 0 = off, 1 = on + uiAttributeStylisticAlternative8, // 0 = off, 1 = on + uiAttributeStylisticAlternative9, // 0 = off, 1 = on + uiAttributeStylisticAlternative10, // 0 = off, 1 = on + uiAttributeStylisticAlternative11, // 0 = off, 1 = on + uiAttributeStylisticAlternative12, // 0 = off, 1 = on + uiAttributeStylisticAlternative13, // 0 = off, 1 = on + uiAttributeStylisticAlternative14, // 0 = off, 1 = on + uiAttributeStylisticAlternative15, // 0 = off, 1 = on + uiAttributeStylisticAlternative16, // 0 = off, 1 = on + uiAttributeStylisticAlternative17, // 0 = off, 1 = on + uiAttributeStylisticAlternative18, // 0 = off, 1 = on + uiAttributeStylisticAlternative19, // 0 = off, 1 = on + uiAttributeStylisticAlternative20, // 0 = off, 1 = on + + uiAttributeContextualAlternates, // 0 = off, 1 = on + uiAttributeSwashes, // 0 = off, 1 = on + uiAttributeContextualSwashes, // 0 = off, 1 = on + + uiAttributeLowercaseCapForms, // enum uiAttributeCapForm + uiAttributeUppercaseCapForms, // enum uiAttributeCapForm + + // TODO kCJKRomanSpacingType + + // TODO uiAttributeSystem, (this might not be doable with DirectWrite) + // TODO uiAttributeCustom, +}; diff --git a/_future/verticaltext/README b/_future/verticaltext/README index d60f3629..1bac02e5 100644 --- a/_future/verticaltext/README +++ b/_future/verticaltext/README @@ -5,6 +5,7 @@ http://www.unicode.org/notes/tn22/RobustVerticalLayout.pdf http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt In addition, with Core Text, the vertical forms attribute vertically centers the vertical glyphs on the bhorizontal baseline, rather than flush with the text. Using the baseline class attribute doesn't seem to work. +TODO investigate kCJKVerticalRomanPlacementType If readded, this will need to be a layout-wide setting, not a per-character setting. Pango works right this way; the current Pango code doesn't seem to work. diff --git a/ui_attrstr.h b/ui_attrstr.h index 7bc7cd1a..3206efe4 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -11,31 +11,10 @@ _UI_ENUM(uiAttribute) { uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A - // TODO kerning amount - // OS X: kCTKernAttributeName - // > 0: farther (TODO from advance or standard kerning?) - // == 0: no kerning - // < 0: closer (TODO same) - // undefined: standard kerning - // Pango: pango_attr_letter_spacing_new() - // parameter meaning unspecified - // Windows: requires Platform Update, SetLetterSpacing() - // parameter meaning unspecified - uiAttributeUnderline, // enum uiDrawUnderlineStyle - // TODO what is the color in the case we don't specify it, black or the text color? + // TODO ensure the color in the case we don't specify it is the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor - // TODO kCTSuperscriptAttributeName vs below - // all it does is set the below attribute so - - // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName - - // TODO strikethroughs? (pango yes, directwrite yes, os x no) - // TODO baseline offsets? (pango yes) - // TODO size scales? (pango yes) - // TODO fallbacks (pango: enable or disable) - // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility uiAttributeLanguage, // BCP 47 string @@ -56,8 +35,6 @@ _UI_ENUM(uiAttribute) { // enumeration values are provided in comments for // documentation purposes. - // TODO kAllTypographicFeaturesType - // AAT calls these "common ligatures" uiAttributeStandardLigatures, // 0 = off, 1 = on uiAttributeRequiredLigatures, // 0 = off, 1 = on @@ -66,19 +43,11 @@ _UI_ENUM(uiAttribute) { uiAttributeContextualLigatures, // 0 = off, 1 = on uiAttributeHistoricalLigatures, // 0 = off, 1 = on - // TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all - uiAttributeUnicase, // 0 = off, 1 = on - // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on - // TODO rename this uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing - // TODO kSmartSwashType, falt and jalt - - // TODO kDiacriticsType - uiAttributeSuperscripts, // enum uiAttributeSuperscript uiAttributeFractionForms, // enum uiAttributeFractionForm @@ -115,9 +84,6 @@ _UI_ENUM(uiAttribute) { // OpenType calls these "old-style" uiAttributeLowercaseNumbers, // 0 = off, 1 = on - // TODO kTextSpacingType - // see kKanaSpacingType below - uiAttributeHanjaToHangul, // 0 = off, 1 = on // AAT defines the following values: @@ -136,17 +102,8 @@ _UI_ENUM(uiAttribute) { uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? - // TODO kKanaSpacingType - // TODO kIdeographicSpacingType - // can they be provided independently of kTextSpacingType? Core Text doesn't seem to - - // TODO kUnicodeDecompositionType - uiAttributeRubyKanaForms, // 0 = off, 1 = on - // TODO kCJKVerticalRomanPlacementType - // this is 'valt' in OpenType but I don't know if I want to make it selectable or not - uiAttributeCJKRomansToItalics, // 0 = off, 1 = on // AAT calls this "case-sensitive layout" @@ -186,11 +143,6 @@ _UI_ENUM(uiAttribute) { uiAttributeLowercaseCapForms, // enum uiAttributeCapForm uiAttributeUppercaseCapForms, // enum uiAttributeCapForm - - // TODO kCJKRomanSpacingType - - // TODO uiAttributeSystem, (this might not be doable with DirectWrite) - // TODO uiAttributeCustom, }; _UI_ENUM(uiDrawUnderlineStyle) { From 0df8346bffd970be166d4004878cad3311dd1573 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 12:12:03 -0500 Subject: [PATCH 199/487] Also removed uiAttributeLangauge for compatibility reasons too. Let's settle all the TODOs now. --- _future/textlanguageattr/README | 1 + _future/textlanguageattr/attrstr_darwin.m | 19 ++++ _future/textlanguageattr/attrstr_unix.c | 9 ++ _future/textlanguageattr/attrstr_windows.cpp | 10 ++ _future/textlanguageattr/common_attrlist.c | 2 + _future/textlanguageattr/drawtext_example.c | 27 +++++ _future/textlanguageattr/fontmatch_darwin.m | 112 +++++++++++++++++++ _future/textlanguageattr/ui.h | 5 + _future/verticaltext/README | 3 + common/attrlist.c | 3 - darwin/attrstr.m | 12 +- darwin/fontmatch.m | 60 +--------- darwin/uipriv_darwin.h | 2 +- examples/drawtext/attributes.c | 26 ----- ui_attrstr.h | 4 - unix/attrstr.c | 9 -- windows/attrstr.cpp | 10 -- 17 files changed, 191 insertions(+), 123 deletions(-) create mode 100644 _future/textlanguageattr/README create mode 100644 _future/textlanguageattr/attrstr_darwin.m create mode 100644 _future/textlanguageattr/attrstr_unix.c create mode 100644 _future/textlanguageattr/attrstr_windows.cpp create mode 100644 _future/textlanguageattr/common_attrlist.c create mode 100644 _future/textlanguageattr/drawtext_example.c create mode 100644 _future/textlanguageattr/fontmatch_darwin.m create mode 100644 _future/textlanguageattr/ui.h diff --git a/_future/textlanguageattr/README b/_future/textlanguageattr/README new file mode 100644 index 00000000..f3bb4916 --- /dev/null +++ b/_future/textlanguageattr/README @@ -0,0 +1 @@ +Removed because proper support on OS X doesn't come until 10.9 unless we use a font with an ltag table; none of the fonts I have come with ltag tables (none of the fonts on OS X do, or at least don't come with a sr entry in their ltag table, and OpenType has replaced ltag with what appears to be custom sub-tables of the GPOS and GSUB tables.) diff --git a/_future/textlanguageattr/attrstr_darwin.m b/_future/textlanguageattr/attrstr_darwin.m new file mode 100644 index 00000000..1717d875 --- /dev/null +++ b/_future/textlanguageattr/attrstr_darwin.m @@ -0,0 +1,19 @@ +struct fontParams { + uiDrawFontDescriptor desc; + uint16_t featureTypes[maxFeatures]; + uint16_t featureSelectors[maxFeatures]; + size_t nFeatures; + const char *language; +}; + + + // locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc + case uiAttributeLanguage: + // LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->language = (const char *) (spec->Value); + }); + break; + + desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language); diff --git a/_future/textlanguageattr/attrstr_unix.c b/_future/textlanguageattr/attrstr_unix.c new file mode 100644 index 00000000..47d22b15 --- /dev/null +++ b/_future/textlanguageattr/attrstr_unix.c @@ -0,0 +1,9 @@ + PangoLanguage *lang; + + // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt + case uiAttributeLanguage: + lang = pango_language_from_string((const char *) (spec->Value)); + addattr(p, start, end, + pango_attr_language_new(lang)); + // lang *cannot* be freed + break; diff --git a/_future/textlanguageattr/attrstr_windows.cpp b/_future/textlanguageattr/attrstr_windows.cpp new file mode 100644 index 00000000..f8238c7f --- /dev/null +++ b/_future/textlanguageattr/attrstr_windows.cpp @@ -0,0 +1,10 @@ + WCHAR *localeName; + + // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt + case uiAttributeLanguage: + localeName = toUTF16((char *) (spec->Value)); + hr = p->layout->SetLocaleName(localeName, range); + if (hr != S_OK) + logHRESULT(L"error applying locale name attribute", hr); + uiFree(localeName); + break; \ No newline at end of file diff --git a/_future/textlanguageattr/common_attrlist.c b/_future/textlanguageattr/common_attrlist.c new file mode 100644 index 00000000..680a2d09 --- /dev/null +++ b/_future/textlanguageattr/common_attrlist.c @@ -0,0 +1,2 @@ + case uiAttributeLanguage: + return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); diff --git a/_future/textlanguageattr/drawtext_example.c b/_future/textlanguageattr/drawtext_example.c new file mode 100644 index 00000000..e006e7e1 --- /dev/null +++ b/_future/textlanguageattr/drawtext_example.c @@ -0,0 +1,27 @@ +before "or any combination of the above" + + // thanks to https://twitter.com/codeman38/status/831924064012886017 + next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; + uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "ru"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " to "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "sr"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/_future/textlanguageattr/fontmatch_darwin.m b/_future/textlanguageattr/fontmatch_darwin.m new file mode 100644 index 00000000..cd4df7a1 --- /dev/null +++ b/_future/textlanguageattr/fontmatch_darwin.m @@ -0,0 +1,112 @@ +// note: this doesn't work for languages; we have to parse the ltag table + +// fortunately features that aren't supported are simply ignored, so we can copy them all in +// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore +// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language) +{ + CTFontDescriptorRef new; + CFMutableArrayRef outerArray; + CFDictionaryRef innerDict; + CFNumberRef numType, numSelector; + const void *keys[2], *values[2]; + size_t i; + CFArrayRef languages; + CFIndex il, nl; + CFStringRef curlang; + char d[2]; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) { + // TODO + } + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; + for (i = 0; i < n; i++) { + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (types + i)); + numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (selectors + i)); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + + // now we have to take care of the language + if (language != NULL) { + languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); + if (languages != NULL) { + nl = CFArrayGetCount(languages); + d[0] = language[0]; + if (d[0] >= 'A' && d[0] <= 'Z') + d[0] += 'a' - 'A'; + d[1] = language[1]; + if (d[1] >= 'A' && d[1] <= 'Z') + d[1] += 'a' - 'A'; + for (il = 0; il < nl; il++) { + char c[2]; + + curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); + // TODO check for failure + CFStringGetBytes(curlang, CFRangeMake(0, 2), + kCFStringEncodingUTF8, 0, false, + (UInt8 *) c, 2, NULL); + if (c[0] >= 'A' && c[0] <= 'Z') + c[0] += 'a' - 'A'; + if (c[1] >= 'A' && c[1] <= 'Z') + c[1] += 'a' - 'A'; + if (c[0] == d[0] && c[1] == d[1]) + break; + } + if (il != nl) { + uint16_t typ; + + typ = kLanguageTagType; + il++; + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&typ)); + numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, + &il); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + CFRelease(languages); + } + } + + keys[0] = kCTFontFeatureSettingsAttribute; + values[0] = outerArray; + innerDict = CFDictionaryCreate(NULL, + keys, values, 1, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(outerArray); + new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict); + CFRelease(desc); + CFRelease(innerDict); + return new; +} diff --git a/_future/textlanguageattr/ui.h b/_future/textlanguageattr/ui.h new file mode 100644 index 00000000..2c3c9ec7 --- /dev/null +++ b/_future/textlanguageattr/ui.h @@ -0,0 +1,5 @@ +after UnderlineColor, before feature tags + + // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility + uiAttributeLanguage, // BCP 47 string diff --git a/_future/verticaltext/README b/_future/verticaltext/README index 1bac02e5..7f30a15c 100644 --- a/_future/verticaltext/README +++ b/_future/verticaltext/README @@ -12,3 +12,6 @@ If readded, this will need to be a layout-wide setting, not a per-character sett More links: https://www.w3.org/TR/2012/NOTE-jlreq-20120403/#line-composition https://www.w3.org/TR/REC-CSS2/notes.html + +TODO indicate where in the attributes.c file that block of code should go (or drop it entirely for the reasons listed above) +TODO same for ui.h diff --git a/common/attrlist.c b/common/attrlist.c index 20aaf97f..7ea65fa6 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -342,9 +342,6 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; - case uiAttributeLanguage: - return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); - // TODO // TODO use boolsEqual() on boolean features } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index f4644b37..3e5797b4 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -74,7 +74,6 @@ struct fontParams { uint16_t featureTypes[maxFeatures]; uint16_t featureSelectors[maxFeatures]; size_t nFeatures; - const char *language; }; static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -258,15 +257,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; - // locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc - case uiAttributeLanguage: - // LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->language = (const char *) (spec->Value); - }); - break; - // TODO default: // handle typographic features ap.p = p; @@ -285,7 +275,7 @@ static CTFontRef fontdescToCTFont(struct fontParams *fp) CTFontRef font; desc = fontdescToCTFontDescriptor(&(fp->desc)); - desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language); + desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures); font = CTFontCreateWithFontDescriptor(desc, fp->desc.Size, NULL); CFRelease(desc); // TODO correct? return font; diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 9acb2edf..0c87a427 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -257,7 +257,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) // fortunately features that aren't supported are simply ignored, so we can copy them all in // LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore // LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! -CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language) +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n) { CTFontDescriptorRef new; CFMutableArrayRef outerArray; @@ -265,10 +265,6 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 CFNumberRef numType, numSelector; const void *keys[2], *values[2]; size_t i; - CFArrayRef languages; - CFIndex il, nl; - CFStringRef curlang; - char d[2]; outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (outerArray == NULL) { @@ -297,60 +293,6 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 CFRelease(numType); } - // now we have to take care of the language - if (language != NULL) { - languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); - if (languages != NULL) { - nl = CFArrayGetCount(languages); - d[0] = language[0]; - if (d[0] >= 'A' && d[0] <= 'Z') - d[0] += 'a' - 'A'; - d[1] = language[1]; - if (d[1] >= 'A' && d[1] <= 'Z') - d[1] += 'a' - 'A'; - for (il = 0; il < nl; il++) { - char c[2]; - - curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); - // TODO check for failure - CFStringGetBytes(curlang, CFRangeMake(0, 2), - kCFStringEncodingUTF8, 0, false, - (UInt8 *) c, 2, NULL); - if (c[0] >= 'A' && c[0] <= 'Z') - c[0] += 'a' - 'A'; - if (c[1] >= 'A' && c[1] <= 'Z') - c[1] += 'a' - 'A'; - if (c[0] == d[0] && c[1] == d[1]) - break; - } - if (il != nl) { - uint16_t typ; - - typ = kLanguageTagType; - il++; - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&typ)); - numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, - &il); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); - } - CFRelease(languages); - } - } - keys[0] = kCTFontFeatureSettingsAttribute; values[0] = outerArray; innerDict = CFDictionaryCreate(NULL, diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 281a4b57..c3de53e2 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -142,7 +142,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index b4a697a8..fbcdd797 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -130,32 +130,6 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - // thanks to https://twitter.com/codeman38/status/831924064012886017 - next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; - uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeItalic; - spec.Value = uiDrawTextItalicItalic; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - spec.Type = uiAttributeLanguage; - spec.Value = (uintptr_t) "ru"; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " to "); - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeItalic; - spec.Value = uiDrawTextItalicItalic; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - spec.Type = uiAttributeLanguage; - spec.Value = (uintptr_t) "sr"; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)"); - - uiAttributedStringAppendUnattributed(attrstr, ", "); - // TODO randomize these ranges better // TODO make some overlap to test that // TODO also change colors to light foreground dark background diff --git a/ui_attrstr.h b/ui_attrstr.h index 3206efe4..0fb4a586 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -15,10 +15,6 @@ _UI_ENUM(uiAttribute) { // TODO ensure the color in the case we don't specify it is the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor - // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) - // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility - uiAttributeLanguage, // BCP 47 string - // These attributes represent typographic features. Each feature // is a separate attribute, to make composition easier. The // availability of for each attribute are defined by the font; the diff --git a/unix/attrstr.c b/unix/attrstr.c index cf5bc509..88eabef4 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -142,7 +142,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; PangoUnderline underline; - PangoLanguage *lang; struct otParam op; switch (spec->Type) { @@ -226,14 +225,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt - case uiAttributeLanguage: - lang = pango_language_from_string((const char *) (spec->Value)); - addattr(p, start, end, - pango_attr_language_new(lang)); - // lang *cannot* be freed - break; - // TODO default: // handle typographic features op.p = p; diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index d0a4b3cc..ad2fad0e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -112,7 +112,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; - WCHAR *localeName; struct otParam op; HRESULT hr; @@ -230,15 +229,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt - case uiAttributeLanguage: - localeName = toUTF16((char *) (spec->Value)); - hr = p->layout->SetLocaleName(localeName, range); - if (hr != S_OK) - logHRESULT(L"error applying locale name attribute", hr); - uiFree(localeName); - break; - // TODO default: // handle typographic features op.p = p; From 0db03e5f440fadeeca9e73d428983240fb1a9000 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 12:26:04 -0500 Subject: [PATCH 200/487] More TODO and documentation cleanup, including removing redundant or now-pointless TODOs. --- _future/verticaltext/README | 2 ++ ui_attrstr.h | 18 +++++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/_future/verticaltext/README b/_future/verticaltext/README index 7f30a15c..99310823 100644 --- a/_future/verticaltext/README +++ b/_future/verticaltext/README @@ -15,3 +15,5 @@ https://www.w3.org/TR/REC-CSS2/notes.html TODO indicate where in the attributes.c file that block of code should go (or drop it entirely for the reasons listed above) TODO same for ui.h + +TODO vertical carets diff --git a/ui_attrstr.h b/ui_attrstr.h index 0fb4a586..6f23fa0c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -276,7 +276,6 @@ _UI_ENUM(uiDrawTextStretch) { struct uiDrawFontDescriptor { char *Family; - // TODO rename to PointSize? double Size; uiDrawTextWeight Weight; uiDrawTextItalic Italic; @@ -301,9 +300,15 @@ struct uiDrawTextLayoutParams { uiDrawTextLayoutAlign Align; }; +// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. +// The above values are listed in vertical order, from top to bottom. +// Ascent + Descent + Leading will give you the typographic bounds +// of the text. BaselineY is the boundary between Ascent and Descent. +// X, Y, and BaselineY are all in the layout's coordinate system, so the +// start point of the baseline will be at (X, BaselineY). All values are +// nonnegative. struct uiDrawTextLayoutLineMetrics { // This describes the overall bounding box of the line. - // TODO figure out if X is correct regardless of both alignment and writing direction double X; double Y; double Width; @@ -322,13 +327,6 @@ struct uiDrawTextLayoutLineMetrics { double LineSpacing; double ParagraphSpacing; - // Height should equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. - // The above values are listed in vertical order, from top to bottom. - // Ascent + Descent + Leading will give you the typographic bounds of the text. - // BaselineY will be the boundary between Ascent and Descent. - // X, Y, and BaselineY are all in the layout's coordinate system, so the start point of the baseline will be at (X, BaselineY). - // All values will be nonnegative. - // TODO trailing whitespace? }; @@ -375,13 +373,11 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); -// TODO vertical carets _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) -// TODO document this returns a new font _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); From ce1a54a9d12e2cb3ea6bc5eb80354b80a4ae1d74 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 12:39:29 -0500 Subject: [PATCH 201/487] More documentation works. --- ui_attrstr.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 6f23fa0c..57f3e79a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,5 +1,22 @@ +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features like superscripts and small caps. +// These attributes can be combined in a variety of ways. +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. typedef struct uiAttributedString uiAttributedString; -typedef struct uiAttributeSpec uiAttributeSpec; // Note: where you say "1 = on", any nonzero value means "on". (TODO) _UI_ENUM(uiAttribute) { @@ -194,7 +211,7 @@ _UI_ENUM(uiAttributeCapForm) { uiAttributeCapFormPetiteCaps, }; -// TODO number case +typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { uiAttribute Type; From f6e9da916a6501531b831445deb728c74151168d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 14:25:16 -0500 Subject: [PATCH 202/487] Fixed Unix cursor positioning. --- unix/drawtext.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 8b45530a..d7a1d3ce 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -218,6 +218,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p int p, trailing; int i; + // this is layout-global, so it takes line origins into account pango_layout_xy_to_index(tl->layout, cairoToPango(x), cairoToPango(y), &p, &trailing); @@ -264,7 +265,8 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int } pango_layout_line_index_to_x(pll, pos, trailing, &pangox); // TODO unref pll? - return pangoToCairo(pangox); + // this is relative to the beginning of the line + return pangoToCairo(pangox) + tl->lineMetrics[line].X; } // note: we can't use gtk_render_insertion_cursor() because that doesn't take information about what line to render on From cca4db5ce9eec791070958848f6445a3352d3049 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:15:20 -0500 Subject: [PATCH 203/487] More TODO resolution and pointless TODO elimination. --- _future/otherattributes/ui.h | 2 +- common/opentype.c | 2 +- darwin/aat.m | 2 +- examples/drawtext/attributes.c | 6 +++--- ui_attrstr.h | 6 ++---- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/_future/otherattributes/ui.h b/_future/otherattributes/ui.h index 89a0c5a1..0d931033 100644 --- a/_future/otherattributes/ui.h +++ b/_future/otherattributes/ui.h @@ -129,7 +129,7 @@ _UI_ENUM(uiAttribute) { // 9 = inverted box // 10 = inverted rounded box // TODO rename to AnnotatedForms? - uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? // TODO kKanaSpacingType diff --git a/common/opentype.c b/common/opentype.c index a3e4f852..8db4dc4c 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -144,7 +144,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeHanjaToHangul: boolspec(spec, "hngl", f, data); return; - case uiAttributeGlyphAnnotations: + case uiAttributeAnnotatedGlyphForms: (*f)("nalt", (uint32_t) (spec->Value), data); return; case uiAttributeRubyKanaForms: diff --git a/darwin/aat.m b/darwin/aat.m index 9aab5f89..e2b3595c 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -162,7 +162,7 @@ int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) if (spec->Value != 0) (*f)(kTransliterationType, kHanjaToHangulSelector, data); return 1; - case uiAttributeGlyphAnnotations: + case uiAttributeAnnotatedGlyphForms: (*f)(kAnnotationType, (uint16_t) (spec->Value), data); return 1; case uiAttributeRubyKanaForms: diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index fbcdd797..b9845605 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -483,21 +483,21 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeGlyphAnnotations; + spec.Type = uiAttributeAnnotatedGlyphForms; spec.Value = 0; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeGlyphAnnotations; + spec.Type = uiAttributeAnnotatedGlyphForms; spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeGlyphAnnotations; + spec.Type = uiAttributeAnnotatedGlyphForms; spec.Value = 4; // AAT inverted circle uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); diff --git a/ui_attrstr.h b/ui_attrstr.h index 57f3e79a..e4193b05 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -29,7 +29,7 @@ _UI_ENUM(uiAttribute) { uiAttributeBackground, // use R, G, B, A uiAttributeUnderline, // enum uiDrawUnderlineStyle - // TODO ensure the color in the case we don't specify it is the text color? + // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // These attributes represent typographic features. Each feature @@ -85,7 +85,6 @@ _UI_ENUM(uiAttribute) { // following values: // 0 = none // OpenType calls this "access all alternates". - // TODO doesn't OpenType do the same about 0? uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? @@ -111,8 +110,7 @@ _UI_ENUM(uiAttribute) { // 8 = diamond // 9 = inverted box // 10 = inverted rounded box - // TODO rename to AnnotatedForms? - uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? uiAttributeRubyKanaForms, // 0 = off, 1 = on From 28b30367e29845e1ab3d14cf994623728dd0d784 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:19:34 -0500 Subject: [PATCH 204/487] Still more TODO resolution. --- _future/otherattributes/ui.h | 40 ++++++++++++++++----------------- common/opentype.c | 40 ++++++++++++++++----------------- darwin/aat.m | 40 ++++++++++++++++----------------- examples/drawtext/attributes.c | 2 +- ui_attrstr.h | 41 +++++++++++++++++----------------- 5 files changed, 81 insertions(+), 82 deletions(-) diff --git a/_future/otherattributes/ui.h b/_future/otherattributes/ui.h index 0d931033..697f09bc 100644 --- a/_future/otherattributes/ui.h +++ b/_future/otherattributes/ui.h @@ -155,26 +155,26 @@ _UI_ENUM(uiAttribute) { // TODO "Alternate"? unify all this // TODO document that these are guaranteed to be consecutive - uiAttributeStylisticAlternative1, // 0 = off, 1 = on - uiAttributeStylisticAlternative2, // 0 = off, 1 = on - uiAttributeStylisticAlternative3, // 0 = off, 1 = on - uiAttributeStylisticAlternative4, // 0 = off, 1 = on - uiAttributeStylisticAlternative5, // 0 = off, 1 = on - uiAttributeStylisticAlternative6, // 0 = off, 1 = on - uiAttributeStylisticAlternative7, // 0 = off, 1 = on - uiAttributeStylisticAlternative8, // 0 = off, 1 = on - uiAttributeStylisticAlternative9, // 0 = off, 1 = on - uiAttributeStylisticAlternative10, // 0 = off, 1 = on - uiAttributeStylisticAlternative11, // 0 = off, 1 = on - uiAttributeStylisticAlternative12, // 0 = off, 1 = on - uiAttributeStylisticAlternative13, // 0 = off, 1 = on - uiAttributeStylisticAlternative14, // 0 = off, 1 = on - uiAttributeStylisticAlternative15, // 0 = off, 1 = on - uiAttributeStylisticAlternative16, // 0 = off, 1 = on - uiAttributeStylisticAlternative17, // 0 = off, 1 = on - uiAttributeStylisticAlternative18, // 0 = off, 1 = on - uiAttributeStylisticAlternative19, // 0 = off, 1 = on - uiAttributeStylisticAlternative20, // 0 = off, 1 = on + uiAttributeStylisticAlternate1, // 0 = off, 1 = on + uiAttributeStylisticAlternate2, // 0 = off, 1 = on + uiAttributeStylisticAlternate3, // 0 = off, 1 = on + uiAttributeStylisticAlternate4, // 0 = off, 1 = on + uiAttributeStylisticAlternate5, // 0 = off, 1 = on + uiAttributeStylisticAlternate6, // 0 = off, 1 = on + uiAttributeStylisticAlternate7, // 0 = off, 1 = on + uiAttributeStylisticAlternate8, // 0 = off, 1 = on + uiAttributeStylisticAlternate9, // 0 = off, 1 = on + uiAttributeStylisticAlternate10, // 0 = off, 1 = on + uiAttributeStylisticAlternate11, // 0 = off, 1 = on + uiAttributeStylisticAlternate12, // 0 = off, 1 = on + uiAttributeStylisticAlternate13, // 0 = off, 1 = on + uiAttributeStylisticAlternate14, // 0 = off, 1 = on + uiAttributeStylisticAlternate15, // 0 = off, 1 = on + uiAttributeStylisticAlternate16, // 0 = off, 1 = on + uiAttributeStylisticAlternate17, // 0 = off, 1 = on + uiAttributeStylisticAlternate18, // 0 = off, 1 = on + uiAttributeStylisticAlternate19, // 0 = off, 1 = on + uiAttributeStylisticAlternate20, // 0 = off, 1 = on uiAttributeContextualAlternates, // 0 = off, 1 = on uiAttributeSwashes, // 0 = off, 1 = on diff --git a/common/opentype.c b/common/opentype.c index 8db4dc4c..3b2ddf64 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -165,64 +165,64 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeAlternateVerticalKana: boolspec(spec, "vkna", f, data); return; - case uiAttributeStylisticAlternative1: + case uiAttributeStylisticAlternate1: boolspec(spec, "ss01", f, data); return; - case uiAttributeStylisticAlternative2: + case uiAttributeStylisticAlternate2: boolspec(spec, "ss02", f, data); return; - case uiAttributeStylisticAlternative3: + case uiAttributeStylisticAlternate3: boolspec(spec, "ss03", f, data); return; - case uiAttributeStylisticAlternative4: + case uiAttributeStylisticAlternate4: boolspec(spec, "ss04", f, data); return; - case uiAttributeStylisticAlternative5: + case uiAttributeStylisticAlternate5: boolspec(spec, "ss05", f, data); return; - case uiAttributeStylisticAlternative6: + case uiAttributeStylisticAlternate6: boolspec(spec, "ss06", f, data); return; - case uiAttributeStylisticAlternative7: + case uiAttributeStylisticAlternate7: boolspec(spec, "ss07", f, data); return; - case uiAttributeStylisticAlternative8: + case uiAttributeStylisticAlternate8: boolspec(spec, "ss08", f, data); return; - case uiAttributeStylisticAlternative9: + case uiAttributeStylisticAlternate9: boolspec(spec, "ss09", f, data); return; - case uiAttributeStylisticAlternative10: + case uiAttributeStylisticAlternate10: boolspec(spec, "ss10", f, data); return; - case uiAttributeStylisticAlternative11: + case uiAttributeStylisticAlternate11: boolspec(spec, "ss11", f, data); return; - case uiAttributeStylisticAlternative12: + case uiAttributeStylisticAlternate12: boolspec(spec, "ss12", f, data); return; - case uiAttributeStylisticAlternative13: + case uiAttributeStylisticAlternate13: boolspec(spec, "ss13", f, data); return; - case uiAttributeStylisticAlternative14: + case uiAttributeStylisticAlternate14: boolspec(spec, "ss14", f, data); return; - case uiAttributeStylisticAlternative15: + case uiAttributeStylisticAlternate15: boolspec(spec, "ss15", f, data); return; - case uiAttributeStylisticAlternative16: + case uiAttributeStylisticAlternate16: boolspec(spec, "ss16", f, data); return; - case uiAttributeStylisticAlternative17: + case uiAttributeStylisticAlternate17: boolspec(spec, "ss17", f, data); return; - case uiAttributeStylisticAlternative18: + case uiAttributeStylisticAlternate18: boolspec(spec, "ss18", f, data); return; - case uiAttributeStylisticAlternative19: + case uiAttributeStylisticAlternate19: boolspec(spec, "ss19", f, data); return; - case uiAttributeStylisticAlternative20: + case uiAttributeStylisticAlternate20: boolspec(spec, "ss20", f, data); return; case uiAttributeContextualAlternates: diff --git a/darwin/aat.m b/darwin/aat.m index e2b3595c..c7fc42e1 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -213,121 +213,121 @@ int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kAlternateVertKanaOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative1: + case uiAttributeStylisticAlternate1: boolspec(spec, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative2: + case uiAttributeStylisticAlternate2: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative3: + case uiAttributeStylisticAlternate3: boolspec(spec, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative4: + case uiAttributeStylisticAlternate4: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative5: + case uiAttributeStylisticAlternate5: boolspec(spec, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative6: + case uiAttributeStylisticAlternate6: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative7: + case uiAttributeStylisticAlternate7: boolspec(spec, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative8: + case uiAttributeStylisticAlternate8: boolspec(spec, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative9: + case uiAttributeStylisticAlternate9: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative10: + case uiAttributeStylisticAlternate10: boolspec(spec, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative11: + case uiAttributeStylisticAlternate11: boolspec(spec, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative12: + case uiAttributeStylisticAlternate12: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative13: + case uiAttributeStylisticAlternate13: boolspec(spec, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative14: + case uiAttributeStylisticAlternate14: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative15: + case uiAttributeStylisticAlternate15: boolspec(spec, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative16: + case uiAttributeStylisticAlternate16: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative17: + case uiAttributeStylisticAlternate17: boolspec(spec, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative18: + case uiAttributeStylisticAlternate18: boolspec(spec, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative19: + case uiAttributeStylisticAlternate19: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative20: + case uiAttributeStylisticAlternate20: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index b9845605..7fb23fe5 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -616,7 +616,7 @@ static void setupAttributedString(void) next = "g"; uiAttributedStringAppendUnattributed(attrstr, "stylistic alternates ("); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeStylisticAlternative1; + spec.Type = uiAttributeStylisticAlternate1; spec.Value = 1; for (i = 0; i < 20; i++) { start = uiAttributedStringLen(attrstr); diff --git a/ui_attrstr.h b/ui_attrstr.h index e4193b05..f5f153c2 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -125,28 +125,27 @@ _UI_ENUM(uiAttribute) { uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on uiAttributeAlternateVerticalKana, // 0 = off, 1 = on - // TODO "Alternate"? unify all this // TODO document that these are guaranteed to be consecutive - uiAttributeStylisticAlternative1, // 0 = off, 1 = on - uiAttributeStylisticAlternative2, // 0 = off, 1 = on - uiAttributeStylisticAlternative3, // 0 = off, 1 = on - uiAttributeStylisticAlternative4, // 0 = off, 1 = on - uiAttributeStylisticAlternative5, // 0 = off, 1 = on - uiAttributeStylisticAlternative6, // 0 = off, 1 = on - uiAttributeStylisticAlternative7, // 0 = off, 1 = on - uiAttributeStylisticAlternative8, // 0 = off, 1 = on - uiAttributeStylisticAlternative9, // 0 = off, 1 = on - uiAttributeStylisticAlternative10, // 0 = off, 1 = on - uiAttributeStylisticAlternative11, // 0 = off, 1 = on - uiAttributeStylisticAlternative12, // 0 = off, 1 = on - uiAttributeStylisticAlternative13, // 0 = off, 1 = on - uiAttributeStylisticAlternative14, // 0 = off, 1 = on - uiAttributeStylisticAlternative15, // 0 = off, 1 = on - uiAttributeStylisticAlternative16, // 0 = off, 1 = on - uiAttributeStylisticAlternative17, // 0 = off, 1 = on - uiAttributeStylisticAlternative18, // 0 = off, 1 = on - uiAttributeStylisticAlternative19, // 0 = off, 1 = on - uiAttributeStylisticAlternative20, // 0 = off, 1 = on + uiAttributeStylisticAlternate1, // 0 = off, 1 = on + uiAttributeStylisticAlternate2, // 0 = off, 1 = on + uiAttributeStylisticAlternate3, // 0 = off, 1 = on + uiAttributeStylisticAlternate4, // 0 = off, 1 = on + uiAttributeStylisticAlternate5, // 0 = off, 1 = on + uiAttributeStylisticAlternate6, // 0 = off, 1 = on + uiAttributeStylisticAlternate7, // 0 = off, 1 = on + uiAttributeStylisticAlternate8, // 0 = off, 1 = on + uiAttributeStylisticAlternate9, // 0 = off, 1 = on + uiAttributeStylisticAlternate10, // 0 = off, 1 = on + uiAttributeStylisticAlternate11, // 0 = off, 1 = on + uiAttributeStylisticAlternate12, // 0 = off, 1 = on + uiAttributeStylisticAlternate13, // 0 = off, 1 = on + uiAttributeStylisticAlternate14, // 0 = off, 1 = on + uiAttributeStylisticAlternate15, // 0 = off, 1 = on + uiAttributeStylisticAlternate16, // 0 = off, 1 = on + uiAttributeStylisticAlternate17, // 0 = off, 1 = on + uiAttributeStylisticAlternate18, // 0 = off, 1 = on + uiAttributeStylisticAlternate19, // 0 = off, 1 = on + uiAttributeStylisticAlternate20, // 0 = off, 1 = on uiAttributeContextualAlternates, // 0 = off, 1 = on uiAttributeSwashes, // 0 = off, 1 = on From 5fe4e27c5d3931053388ae6d8d957a8695e21fe3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:46:53 -0500 Subject: [PATCH 205/487] TODO cleanup and stale TODO removal. That nLines == 0 TODO needs more testing... --- darwin/drawtext.m | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c0123117..579d3863 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -37,7 +37,7 @@ struct uiDrawTextLayout { size_t nUTF16; }; -// TODO this is wrong for our hit-test example's multiple combining character example +// TODO document that lines may or may not overlap because ours do in the case of multiple combining characters static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; @@ -65,7 +65,6 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) ascent = bounds.size.height + bounds.origin.y; descent = -boundsNoLeading.origin.y; - // TODO does this preserve leading sign? leading = -bounds.origin.y - descent; // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) @@ -101,7 +100,6 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize // go from bottom-left corner to top-left metrics[i].Y -= metrics[i].Height; metrics[i].BaselineY = size.height - metrics[i].BaselineY; - // TODO also adjust by metrics[i].Height? } uiFree(origins); @@ -121,7 +119,6 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) range.length = CFAttributedStringGetLength(tl->attrstr); tl->width = p->Width; - // TODO CTFrameProgression for RTL/LTR // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); if (tl->framesetter == NULL) { @@ -131,8 +128,6 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) cgwidth = (CGFloat) (tl->width); if (cgwidth < 0) cgwidth = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - // TODO double check to make sure this TODO was right tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, range, // TODO kCTFramePathWidthAttributeName? @@ -209,8 +204,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) CGContextRestoreGState(c->c); } -// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral -// TODO width doesn't include trailing whitespace... +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines +// TODO width doesn't include trailing whitespace... (TODO on which platforms?) // TODO figure out how paragraph spacing should play into this void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { @@ -239,8 +234,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } -// TODO note that in some cases lines can overlap slightly -// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing +// in the case of overlapping lines, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing +// TODO should we document this? void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) { int i; From 5234586ead8842f26cd14a72428210fd9dbda983 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:54:35 -0500 Subject: [PATCH 206/487] Added a test for making uiDrawTextLayouts on empty strings. We're already off to a good start since we have a ~0 error on OS X... --- examples/CMakeLists.txt | 1 + examples/drawtext/drawtext.h | 3 + examples/drawtext/emptystr_hittest.c | 254 +++++++++++++++++++++++++++ examples/drawtext/main.c | 5 + 4 files changed, 263 insertions(+) create mode 100644 examples/drawtext/emptystr_hittest.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 64c7098c..e011db23 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -34,6 +34,7 @@ endif() _add_example(drawtext drawtext/attributes.c drawtext/basic.c + drawtext/emptystr_hittest.c drawtext/hittest.c drawtext/main.c ${_EXAMPLE_RESOURCES_RC} diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index 0ac4cb36..072c2f02 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -23,3 +23,6 @@ extern struct example *mkHitTestExample(void); // attributes.c extern struct example *mkAttributesExample(void); + +// emptystr_hittest.c +extern struct example *mkEmptyStringExample(void); diff --git a/examples/drawtext/emptystr_hittest.c b/examples/drawtext/emptystr_hittest.c new file mode 100644 index 00000000..5542e469 --- /dev/null +++ b/examples/drawtext/emptystr_hittest.c @@ -0,0 +1,254 @@ +// 20 january 2017 +#include "drawtext.h" + +// TODO FOR THIS FILE +// get rid of it once we rewrite this example (which requires having more fine-grained scrolling control) + +// TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place +// TODO the hiding and showing does not work properly on GTK+ +// TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that +// TODO make sure to check the cursor positions of RTL on all platforms + +static const char *text = ""; +static char fontFamily[] = "Helvetica"; +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 14, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiAttributedString *attrstr; +static uiDrawTextLayoutParams params; + +#define margins 10 + +static uiBox *panel; +static uiBox *vbox; +static uiLabel *caretLabel; +static uiCheckbox *showLineBounds; +static uiFontButton *fontButton; +static uiCombobox *textAlign; + +static int caretLine = -1; +static size_t caretPos; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + uiDrawTextLayoutLineMetrics m; + + // only clip the text, not the guides + uiDrawSave(p->Context); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); + uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + if (caretLine == -1) { + caretLine = uiDrawTextLayoutNumLines(layout) - 1; + caretPos = uiAttributedStringLen(attrstr); + } + uiDrawCaret(p->Context, margins, margins, + layout, caretPos, &caretLine); + + if (uiCheckboxChecked(showLineBounds)) { + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + } + + uiDrawFreeTextLayout(layout); +} + +static void mouse(uiAreaMouseEvent *e) +{ + uiDrawTextLayout *layout; + char labelText[128]; + + if (e->Down != 1) + return; + + params.Width = e->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); + uiDrawTextLayoutHitTest(layout, + e->X - margins, e->Y - margins, + &caretPos, &caretLine); + uiDrawFreeTextLayout(layout); + + // TODO move this into the draw handler so it is reflected by keyboard-based position changes + // urgh %zd is not supported by MSVC with sprintf() + // TODO get that part in test/ about having no other option + sprintf(labelText, "pos %d line %d", + (int) caretPos, caretLine); + uiLabelSetText(caretLabel, labelText); + + redraw(); +} + +static int key(uiAreaKeyEvent *e) +{ + size_t grapheme; + + if (e->Up) + return 0; + if (e->Key != 0) + return 0; + switch (e->ExtKey) { + case uiExtKeyUp: + // TODO + return 1; + case uiExtKeyDown: + // TODO + return 1; + case uiExtKeyLeft: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == 0) + return 0; + grapheme--; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + case uiExtKeyRight: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == uiAttributedStringNumGraphemes(attrstr)) + return 0; + grapheme++; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + } + return 0; +} + +static struct example hitTestExample; + +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static void changeFont(uiFontButton *b, void *data) +{ + if (defaultFont.Family != fontFamily) + uiFreeText(defaultFont.Family); + // TODO rename defaultFont + uiFontButtonFont(fontButton, &defaultFont); + printf("{\n\tfamily: %s\n\tsize: %g\n\tweight: %d\n\titalic: %d\n\tstretch: %d\n}\n", + defaultFont.Family, + defaultFont.Size, + (int) (defaultFont.Weight), + (int) (defaultFont.Italic), + (int) (defaultFont.Stretch)); + redraw(); +} + +static void changeTextAlign(uiCombobox *c, void *data) +{ + // note the order of the items added below + params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + redraw(); +} + +// TODO share? +static uiCheckbox *newCheckbox(uiBox *box, const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(box, uiControl(c), 0); + return c; +} + +struct example *mkEmptyStringExample(void) +{ + panel = uiNewHorizontalBox(); + vbox = uiNewVerticalBox(); + // TODO the second vbox causes this not to stretch at least on OS X + uiBoxAppend(panel, uiControl(vbox), 1); + caretLabel = uiNewLabel("Caret information is shown here"); + uiBoxAppend(vbox, uiControl(caretLabel), 0); + showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)"); + vbox = uiNewVerticalBox(); + uiBoxAppend(panel, uiControl(vbox), 0); + fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, changeFont, NULL); + // TODO set the font button to the current defaultFont + uiBoxAppend(vbox, uiControl(fontButton), 0); + textAlign = uiNewCombobox(); + // note that these are in the order in the enum + uiComboboxAppend(textAlign, "Left"); + uiComboboxAppend(textAlign, "Center"); + uiComboboxAppend(textAlign, "Right"); + uiComboboxOnSelected(textAlign, changeTextAlign, NULL); + uiBoxAppend(vbox, uiControl(textAlign), 0); + + hitTestExample.name = "Empty String"; + hitTestExample.panel = uiControl(panel); + hitTestExample.draw = draw; + hitTestExample.mouse = mouse; + hitTestExample.key = key; + + attrstr = uiNewAttributedString(text); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; + + return &hitTestExample; +} diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 822382d0..4e72b25e 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -115,6 +115,11 @@ int main(void) uiControlHide(examples[n]->panel); uiBoxAppend(box, examples[n]->panel, 0); n++; + examples[n] = mkEmptyStringExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); + uiBoxAppend(box, examples[n]->panel, 0); + n++; // and set things up for the initial state uiComboboxSetSelected(exampleList, 0); uiComboboxOnSelected(exampleList, onExampleChanged, NULL); From 58fff53f614d9170eb2faef3f3d92abb1773fed1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:18:15 -0500 Subject: [PATCH 207/487] More TODOs... --- common/drawtext.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/drawtext.c b/common/drawtext.c index 938e1aeb..ca66796a 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -2,6 +2,8 @@ #include "../ui.h" #include "uipriv.h" +// TODO this doesn't handle the case where nLines == 0 +// TODO this should never happen even if there are no characters?? // TODO figure out how to make this work on GTK+ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line) { From c4400b83f35fe9242d494fd51812943d649d5eed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:24:52 -0500 Subject: [PATCH 208/487] Okay, notes on linelessness on OS X. --- darwin/drawtext.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 579d3863..c6bd9e86 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -2,7 +2,9 @@ #import "uipriv_darwin.h" #import "draw.h" -// TODO what happens if nLines == 0 in any function? +// TODO on an empty string nLines == 0 +// we must prevent this somehow +// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines struct uiDrawTextLayout { CFAttributedStringRef attrstr; From 3d5fbc0880db25a7ce819af9546a515cb02e24f4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:42:40 -0500 Subject: [PATCH 209/487] Checked empty strings on Pango. --- darwin/drawtext.m | 2 +- unix/drawtext.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c6bd9e86..e5f0d12e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -207,7 +207,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) } // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines -// TODO width doesn't include trailing whitespace... (TODO on which platforms?) +// TODO width doesn't include trailing whitespace... // TODO figure out how paragraph spacing should play into this void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { diff --git a/unix/drawtext.c b/unix/drawtext.c index d7a1d3ce..842b70dd 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,11 +4,13 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? +// - nLines == 0: mostly works, except the width is wrong if the paragraph alignment is center or right... struct uiDrawTextLayout { PangoLayout *layout; GPtrArray *backgroundClosures; uiDrawTextLayoutLineMetrics *lineMetrics; + // TODO change everything to use this int nLines; }; From 70940e5c065102f87d819d01ad5689a224165942 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:43:37 -0500 Subject: [PATCH 210/487] More TODOs. --- darwin/drawtext.m | 1 + unix/drawtext.c | 1 + 2 files changed, 2 insertions(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index e5f0d12e..fd5a5bbb 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -5,6 +5,7 @@ // TODO on an empty string nLines == 0 // we must prevent this somehow // TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines +// TODO what happens to extents if only whitespace? struct uiDrawTextLayout { CFAttributedStringRef attrstr; diff --git a/unix/drawtext.c b/unix/drawtext.c index 842b70dd..6acb848a 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -5,6 +5,7 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? // - nLines == 0: mostly works, except the width is wrong if the paragraph alignment is center or right... +// - TODO check whitespace and line bounds struct uiDrawTextLayout { PangoLayout *layout; From df2a726c1beabf7068674a6a111faad004b87da9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Feb 2017 01:24:43 -0500 Subject: [PATCH 211/487] And fixed on Windows too. We're good. --- windows/drawtext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 67ec0a47..275420da 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -5,7 +5,7 @@ // TODO // - consider the warnings about antialiasing in the PadWrite sample // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... -// - what happens if any nLines == 0? +// - empty string: nLines == 1 and all checks out except extents has x == 0 when not left aligned // - paragraph alignment is subject to RTL mirroring; see if it is on other platforms // TODO verify our renderer is correct, especially with regards to snapping From a0454a6b43ebf2bb705646580a7f25bd422b8a9c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 7 May 2017 10:30:08 -0400 Subject: [PATCH 212/487] Started dropping the whole features system in favor of a homogenous OpenType feature attribute just like every other API. Will make some things easier, hopefully... --- ui_attrstr.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index f5f153c2..0077b6ee 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -32,6 +32,10 @@ _UI_ENUM(uiAttribute) { // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + uiAttributeFeatures, // object of type uiOpenTypeFeatures + +#if 0 + // These attributes represent typographic features. Each feature // is a separate attribute, to make composition easier. The // availability of for each attribute are defined by the font; the @@ -154,6 +158,8 @@ _UI_ENUM(uiAttribute) { uiAttributeLowercaseCapForms, // enum uiAttributeCapForm uiAttributeUppercaseCapForms, // enum uiAttributeCapForm }; +#endif +}; _UI_ENUM(uiDrawUnderlineStyle) { uiDrawUnderlineStyleNone, @@ -169,6 +175,8 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +#if 0 + _UI_ENUM(uiAttributeNumberSpacing) { uiAttributeNumberSpacingProportional, // AAT calls this "monospaced" @@ -208,6 +216,18 @@ _UI_ENUM(uiAttributeCapForm) { uiAttributeCapFormPetiteCaps, }; +#endif + +// TODO rename? +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; +// TODO detailed constructor? +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf); +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); +// TODO remove, query, enumerate +// TODO make the compare function public? + typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { From 676dfb87f2a61e3e4bf24dd2036767beead67a52 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 11 May 2017 10:27:34 -0400 Subject: [PATCH 213/487] Started the uiOpenTypeFeatures implementationss, filling in holes in the API. --- darwin/opentype.m | 52 ++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 11 ++++++++-- unix/opentype.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ windows/opentype.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 darwin/opentype.m create mode 100644 unix/opentype.c create mode 100644 windows/opentype.cpp diff --git a/darwin/opentype.m b/darwin/opentype.m new file mode 100644 index 00000000..4273b80f --- /dev/null +++ b/darwin/opentype.m @@ -0,0 +1,52 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" + +struct uiOpenTypeFeatures { + xxxx; +}; + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiNew(uiOpenTypeFeatures); + xxxx; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + xxxxxx; + uiFree(otf); +} + +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *out; + + out = uiNew(uiOpenTypeFeatures); + xxxxxx; + return out; +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ +} + +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ +} + +int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ +} + +void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ +} + +int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +{ +} + +// TODO put the internal function to produce a backend object from one here diff --git a/ui_attrstr.h b/ui_attrstr.h index 0077b6ee..f7c03b03 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -220,13 +220,19 @@ _UI_ENUM(uiAttributeCapForm) { // TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; +// TODO pass the feature set? +typedef int (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); // TODO detailed constructor? _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); +// TODO put above Free? +// TODO Copy instead of Clone? _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf); _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); -// TODO remove, query, enumerate -// TODO make the compare function public? +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); +_UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); +_UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); +_UI_EXTERN int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; @@ -240,6 +246,7 @@ struct uiAttributeSpec { double A; }; +// TODO name the foreach return values typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor diff --git a/unix/opentype.c b/unix/opentype.c new file mode 100644 index 00000000..4273b80f --- /dev/null +++ b/unix/opentype.c @@ -0,0 +1,52 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" + +struct uiOpenTypeFeatures { + xxxx; +}; + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiNew(uiOpenTypeFeatures); + xxxx; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + xxxxxx; + uiFree(otf); +} + +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *out; + + out = uiNew(uiOpenTypeFeatures); + xxxxxx; + return out; +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ +} + +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ +} + +int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ +} + +void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ +} + +int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +{ +} + +// TODO put the internal function to produce a backend object from one here diff --git a/windows/opentype.cpp b/windows/opentype.cpp new file mode 100644 index 00000000..4273b80f --- /dev/null +++ b/windows/opentype.cpp @@ -0,0 +1,52 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" + +struct uiOpenTypeFeatures { + xxxx; +}; + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiNew(uiOpenTypeFeatures); + xxxx; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + xxxxxx; + uiFree(otf); +} + +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *out; + + out = uiNew(uiOpenTypeFeatures); + xxxxxx; + return out; +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ +} + +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ +} + +int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ +} + +void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ +} + +int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +{ +} + +// TODO put the internal function to produce a backend object from one here From 4e6ccc05f1ed71870a40ede1212de1346be3ac37 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 15:57:39 -0400 Subject: [PATCH 214/487] Implemented the new opentype.cpp on Windows. --- windows/CMakeLists.txt | 1 + windows/opentype.cpp | 63 +++++++++++++++++++++++++++++++++++--- windows/uipriv_windows.hpp | 3 ++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index f4a10998..739c1300 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -38,6 +38,7 @@ list(APPEND _LIBUI_SOURCES windows/main.cpp windows/menu.cpp windows/multilineentry.cpp + windows/opentype.cpp windows/parent.cpp windows/progressbar.cpp windows/radiobuttons.cpp diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 4273b80f..21e681b7 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -1,8 +1,10 @@ // 11 may 2017 #include "uipriv_windows.hpp" +typedef std::map tagmap; + struct uiOpenTypeFeatures { - xxxx; + tagmap *tags; }; uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) @@ -10,13 +12,13 @@ uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) uiOpenTypeFeatures *otf; otf = uiNew(uiOpenTypeFeatures); - xxxx; + otf->tags = new tagmap; return otf; } void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) { - xxxxxx; + delete otf->tags; uiFree(otf); } @@ -25,28 +27,79 @@ uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) uiOpenTypeFeatures *out; out = uiNew(uiOpenTypeFeatures); - xxxxxx; + out->tags = new tagmap; + *(out->tags) = *(otf->tags); return out; } +#define mktag(a, b, c, d) ((uint32_t) DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d)) + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { + (*(otf->tags))[mktag(a, b, c, d)] = value; } +// TODO what should happen if a/b/c/d isn't defined? +// TODO what does std::map do if a/b/c/d isn't defined? void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + otf->tags->erase(mktag(a, b, c, d)); } int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { + tagmap::const_iterator iter; + + iter = otf->tags->find(mktag(a, b, c, d)); + if (iter == otf->tags->end()) + return 0; + *value = iter->second; + return 1; } void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { + tagmap::const_iterator iter, end; + + end = otf->tags->end(); + for (iter = otf->tags->begin(); iter != end; iter++) { + uint8_t a, b, c, d; + + a = (uint8_t) (iter->first & 0xFF); + b = (uint8_t) ((iter->first >> 8) & 0xFF); + c = (uint8_t) ((iter->first >> 16) & 0xFF); + d = (uint8_t) ((iter->first >> 24) & 0xFF); + // TODO handle return value + (*f)((char) a, (char) b, (char) c, (char) d, + iter->second, data); + } } +// TODO wait, is this function even necessary? how do we unify attributes?? int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { + // TODO make sure this is correct + return *(a->tags) == *(b->tags); } -// TODO put the internal function to produce a backend object from one here +IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf) +{ + IDWriteTypography *dt; + tagmap::const_iterator iter, end; + DWRITE_FONT_FEATURE dff; + HRESULT hr; + + hr = dwfactory->CreateTypography(&dt); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTypography", hr); + end = otf->tags->end(); + for (iter = otf->tags->begin(); iter != end; iter++) { + ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); + dff.nameTag = (DWRITE_FONT_FEATURE_TAG) (iter->first); + dff.parameter = (UINT32) (iter->second); + hr = dt->AddFontFeature(dff); + if (hr != S_OK) + logHRESULT(L"error adding OpenType feature to IDWriteTypography", hr); + } + return dt; +} diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index f1de95cf..aa39dd07 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -162,3 +162,6 @@ extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); // drawtext.cpp extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); + +// opentype.cpp +extern IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf); From 2f73df09e3b851c4392f11c72c86b3f8ec172730 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 16:15:54 -0400 Subject: [PATCH 215/487] And adjusted the Windows attrstr.cpp to boot. Now to rewrite the example and test. --- ui_attrstr.h | 1 + windows/attrstr.cpp | 68 +++++++++++--------------------------------- windows/opentype.cpp | 1 - 3 files changed, 17 insertions(+), 53 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index f7c03b03..d66f2e9a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -32,6 +32,7 @@ _UI_ENUM(uiAttribute) { // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout uiAttributeFeatures, // object of type uiOpenTypeFeatures #if 0 diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index ad2fad0e..3b652979 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -2,9 +2,11 @@ #include "uipriv_windows.hpp" #include "draw.hpp" +// TODO this whole file needs cleanup + // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() // we also need to collect all the OpenType features and background blocks and add them all at once -// TODO(TODO does not seem to apply here) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly +// TODO(TODO does not seem to apply here?) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { @@ -38,25 +40,22 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e } } -static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) { + IDWriteTypography *dt; size_t i; - size_t *key; - IDWriteTypography *t; - HRESULT hr; + dt = otfToDirectWrite(otf); for (i = start; i < end; i++) { // don't create redundant entries; see below + // TODO explain this more clearly (surrogate pairs) if (!isCodepointStart(p->s[i])) continue; - t = (*(p->features))[i]; - if (t != NULL) - continue; - hr = dwfactory->CreateTypography(&t); - if (hr != S_OK) - logHRESULT(L"error creating IDWriteTypography", hr); - (*(p->features))[i] = t; + dt->AddRef(); + (*(p->features))[i] = dt; } + // and release the initial reference + dt->Release(); } static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) @@ -73,38 +72,6 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -struct otParam { - struct foreachParams *p; - size_t start; - size_t end; -}; - -static void doOpenType(const char *featureTag, uint32_t param, void *data) -{ - struct otParam *p = (struct otParam *) data; - size_t i; - IDWriteTypography *t; - DWRITE_FONT_FEATURE feature; - HRESULT hr; - - feature.nameTag = (DWRITE_FONT_FEATURE_TAG) DWRITE_MAKE_OPENTYPE_TAG( - featureTag[0], - featureTag[1], - featureTag[2], - featureTag[3]); - feature.parameter = param; - ensureFeaturesInRange(p->p, p->start, p->end); - for (i = p->start; i < p->end; i++) { - // don't use redundant entries; see below - if (!isCodepointStart(p->p->s[i])) - continue; - t = (*(p->p->features))[i]; - hr = t->AddFontFeature(feature); - if (hr != S_OK) - logHRESULT(L"error adding feature to IDWriteTypography", hr); - } -} - static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -112,7 +79,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; - struct otParam op; HRESULT hr; ostart = start; @@ -229,14 +195,12 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - default: - // handle typographic features - op.p = p; - op.start = start; - op.end = end; - // TODO check if unhandled and complain - specToOpenType(spec, doOpenType, &op); + cae uiAttributeOpenTypeFeatures: + setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); break; + default: + // TODO complain + ; } return 0; } diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 21e681b7..f3ca36c3 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -75,7 +75,6 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac } } -// TODO wait, is this function even necessary? how do we unify attributes?? int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { // TODO make sure this is correct From 4f31a1331c04736cbe43477216baae4656f25e5b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 16:18:28 -0400 Subject: [PATCH 216/487] And omitted the common OpenType stuff from the build. --- common/CMakeLists.txt | 1 - common/uipriv.h | 4 ---- 2 files changed, 5 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c0542c82..8ecff8b3 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,7 +8,6 @@ list(APPEND _LIBUI_SOURCES common/debug.c common/drawtext.c common/matrix.c - common/opentype.c common/shouldquit.c common/userbugs.c common/utf.c diff --git a/common/uipriv.h b/common/uipriv.h index 1c61f9c7..553073f5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -101,10 +101,6 @@ struct caretDrawParams { extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); -// opentype.c -typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); -extern void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data); - #ifdef __cplusplus } #endif From 3e28887a24bdb36b64607ab07b1c0589ca4c9a15 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 19:21:27 -0400 Subject: [PATCH 217/487] Fixed the build. --- common/attrlist.c | 4 ++-- windows/attrstr.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 7ea65fa6..ec0b12bd 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -342,9 +342,9 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; - // TODO use boolsEqual() on boolean features } - // handles the rest + // handles the rest, including pointer comparison for uiAttributeFeatures + // TODO rename it to uiAttributeOpenTypeFeatures? return attr->spec.Value == spec->Value; } diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 3b652979..8f5e0adf 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -195,7 +195,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - cae uiAttributeOpenTypeFeatures: + case uiAttributeFeatures: setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); break; default: From 4f427b21218f4d342d06ea85a7761b7543bfeb4d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 21:41:08 -0400 Subject: [PATCH 218/487] And ported the drawtext example to use the new features system. --- examples/drawtext/attributes.c | 264 +++++++++++++++++++-------------- examples/drawtext/drawtext.h | 1 + 2 files changed, 153 insertions(+), 112 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 7fb23fe5..56ed4d13 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -3,12 +3,32 @@ static uiAttributedString *attrstr; +#define nFeatures 256 +static uiOpenTypeFeatures *features[nFeatures]; +static int curFeature = 0; + +static uintptr_t addFeature(const char tag[4], uint32_t value) +{ + uiOpenTypeFeatures *otf; + + if (curFeature >= nFeatures) { + fprintf(stderr, "TODO (also TODO is there a panic function?)\n"); + exit(EXIT_FAILURE); + } + otf = uiNewOpenTypeFeatures(); + uiOpenTypeFeaturesAdd(otf, tag[0], tag[1], tag[2], tag[3], value); + features[curFeature] = otf; + curFeature++; + return (uintptr_t) otf; +} + // some of these examples come from Microsoft's and Apple's lists of typographic features and also https://www.fontfont.com/staticcontent/downloads/FF_OT_User_Guide.pdf static void setupAttributedString(void) { uiAttributeSpec spec; size_t start, end; const char *next; + uiOpenTypeFeatures *otf; int i; attrstr = uiNewAttributedString("uiAttributedString isn't just for plain text! It supports "); @@ -162,6 +182,8 @@ static void setupAttributedString(void) spec.Value = uiDrawUnderlineStyleSingle; uiAttributedStringSetAttribute(attrstr, &spec, start + 9, end - 1); + // TODO rewrite this to talk about OpenTpe instead + // TODO also shorten this to something more useful and that covers the general gist of things (and combines features arbitrarily like the previous demo) when we add a general OpenType demo (see the last TODO in this function) uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); next = "fi"; @@ -169,8 +191,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeStandardLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("liga", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -183,8 +205,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeRequiredLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("rlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "\xE2\x80\xAC)"); @@ -195,8 +217,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeDiscretionaryLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("clig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -207,20 +229,23 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("clig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); + otf = (uiOpenTypeFeatures *) addFeature("hlig", 1); + // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too + uiOpenTypeFeaturesAdd(otf, 'h', 'i', 's', 't', 1); next = "\xC3\x9F"; uiAttributedStringAppendUnattributed(attrstr, "historical ligatures like the decomposition of \xC3\x9F ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHistoricalLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = (uintptr_t) otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -231,8 +256,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeUnicase; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("unic", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -242,15 +267,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeNumberSpacings; - spec.Value = uiAttributeNumberSpacingProportional; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("pnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and tabular/monospaced ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeNumberSpacings; - spec.Value = uiAttributeNumberSpacingTabular; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("tnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") numbers"); @@ -261,8 +286,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptSuperscript; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("sups", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -273,8 +298,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptSubscript; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("subs", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -285,8 +310,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptOrdinal; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ordn", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -297,8 +322,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptScientificInferior; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("sinf", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -308,23 +333,25 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, "fraction forms ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); +#if 0 /* TODO */ uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFractionForms; spec.Value = uiAttributeFractionFormNone; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); +#endif start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeFractionForms; - spec.Value = uiAttributeFractionFormVertical; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("afrc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeFractionForms; - spec.Value = uiAttributeFractionFormDiagonal; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("frac", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -335,15 +362,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSlashedZero; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("zero", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSlashedZero; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("zero", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -354,15 +381,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeMathematicalGreek; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("mgrk", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeMathematicalGreek; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("mgrk", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -374,8 +401,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeOrnamentalForms; - spec.Value = (uintptr_t) i; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ornm", i); uiAttributedStringSetAttribute(attrstr, &spec, start, end); next = "\xE2\x80\xA2"; } @@ -388,15 +415,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSpecificCharacterForm; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("aalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSpecificCharacterForm; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("aalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -407,15 +434,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeTitlingCapitalForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("titl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeTitlingCapitalForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("titl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -426,34 +453,40 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanCharacterForms; - spec.Value = uiAttributeHanCharacterFormJIS1978; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("jp78", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanCharacterForms; - spec.Value = uiAttributeHanCharacterFormJIS1983; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("jp83", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); + otf = (uiOpenTypeFeatures *) addFeature("onum", 0); + // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too + // TODO is it always set? + uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 0); next = "0123456789"; uiAttributedStringAppendUnattributed(attrstr, "lowercase numbers ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseNumbers; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = (uintptr_t) otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseNumbers; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + otf = (uiOpenTypeFeatures *) addFeature("onum", 1); + uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 1); + spec.Value = (uintptr_t) otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -464,15 +497,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanjaToHangul; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hngl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanjaToHangul; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hngl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -483,22 +516,22 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAnnotatedGlyphForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("nalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAnnotatedGlyphForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("nalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAnnotatedGlyphForms; - spec.Value = 4; // AAT inverted circle + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("nalt", 4); // AAT inverted circle uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -509,15 +542,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeRubyKanaForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ruby", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeRubyKanaForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ruby", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -528,15 +561,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCJKRomansToItalics; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ital", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCJKRomansToItalics; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ital", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -547,15 +580,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCaseSensitiveForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("case", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCaseSensitiveForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("case", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -566,15 +599,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCapitalSpacing; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cpsp", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCapitalSpacing; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cpsp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -585,29 +618,29 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateHorizontalKana; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateHorizontalKana; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and vertical ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateVerticalKana; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("vkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateVerticalKana; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("vkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") kana forms"); @@ -616,13 +649,20 @@ static void setupAttributedString(void) next = "g"; uiAttributedStringAppendUnattributed(attrstr, "stylistic alternates ("); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeStylisticAlternate1; - spec.Value = 1; - for (i = 0; i < 20; i++) { + spec.Type = uiAttributeFeatures; + for (i = 1; i <= 20; i++) { + char tag[4]; + + tag[0] = 's'; + tag[1] = 's'; + tag[2] = '0'; + if (i >= 10) + tag[2] = '1'; + tag[3] = (i % 10) + '0'; // TODO see how I wrote this elsewhere start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type++; + spec.Value = addFeature(tag, 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); } uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -634,15 +674,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualAlternates; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("calt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualAlternates; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("calt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -653,15 +693,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSwashes; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("swsh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSwashes; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("swsh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -672,15 +712,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualSwashes; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cswh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualSwashes; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cswh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -690,16 +730,16 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseCapForms; - spec.Value = uiAttributeCapFormSmallCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("smcp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); next = "Petite Caps"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseCapForms; - spec.Value = uiAttributeCapFormPetiteCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("pcap", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -708,16 +748,16 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeUppercaseCapForms; - spec.Value = uiAttributeCapFormSmallCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("c2sp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", and "); next = "PETITE UPPERCASES"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeUppercaseCapForms; - spec.Value = uiAttributeCapFormPetiteCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("c2pc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "."); diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index 072c2f02..484dcec9 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -1,6 +1,7 @@ // 20 january 2017 #include #include +#include #include "../../ui.h" struct example { From ff4ab7110cb8b5ee303130484615145b3ee33acb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 22:56:55 -0400 Subject: [PATCH 219/487] Filled in GTK+ opentype.c. This is gonna suck as much as it does now... --- unix/CMakeLists.txt | 1 + unix/opentype.c | 158 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 6 deletions(-) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 949c4a11..f21256c3 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -34,6 +34,7 @@ list(APPEND _LIBUI_SOURCES unix/main.c unix/menu.c unix/multilineentry.c + unix/opentype.c unix/progressbar.c unix/radiobuttons.c unix/separator.c diff --git a/unix/opentype.c b/unix/opentype.c index 4273b80f..12869a6a 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -1,8 +1,10 @@ // 11 may 2017 #include "uipriv_windows.hpp" +// TODO switch from GINT_FROM_POINTER() and so to a fake GUINT_FROM_POINTER()? + struct uiOpenTypeFeatures { - xxxx; + GHashTable *tags; }; uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) @@ -10,43 +12,187 @@ uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) uiOpenTypeFeatures *otf; otf = uiNew(uiOpenTypeFeatures); - xxxx; + otf->tags = g_hash_table_new(g_direct_hash, g_direct_equal); return otf; } void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) { - xxxxxx; + g_hash_table_destroy(otf->tags); uiFree(otf); } +static void cloneTags(gpointer key, gpointer value, gpointer data) +{ + // TODO is there a G_HASH_TABLE()? + g_hash_table_replace((GHashTable *) data, key, value); +} + uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; - out = uiNew(uiOpenTypeFeatures); - xxxxxx; + // TODO switch the windows one to use this + out = uiNewOpenTypeFeatures(); + g_hash_table_foreach(otf->tags, cloneTags, out->tags); return out; } +static gpointer mkTag(char a, char b, char c, char d) +{ + uint32_t tag; + + tag = (((uint32_t) a) & 0xFF) << 24; + tag |= (((uint32_t) b) & 0xFF) << 16; + tag |= (((uint32_t) c) & 0xFF) << 8; + tag |= ((uint32_t) d) & 0xFF; + return GINT_TO_POINTER(tag); +} + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { + g_hash_table_replace(otf->tags, mkTag(a, b, c, d), GINT_TO_POINTER(value)); } void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); } +// TODO should this be before Add and Remove? +// TODO better name than Get? int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { + gboolean found; + gpointer gv; + + found = g_hash_table_lookup_extended(otf->tags, + mkTag(a, b, c, d), + NULL, &gv); + if (!found) + return 0; + *value = GPOINTER_TO_INT(gv); + return 1; +} + +struct otfForEach { + uiOpenTypeFeaturesForEachFunc f; + void *data; + // TODO store continuation status here +}; + +static void foreach(gpointer key, gpointer value, gpointer data) +{ + struct otfForEach *ofe = (struct otfForEach *) data; + uint32_t tag; + uint8_t a, b, c, d; + + tag = GPOINTER_TO_INT(key); + a = (uint8_t) ((tag >> 24) & 0xFF); + b = (uint8_t) ((tag >> 16) & 0xFF); + c = (uint8_t) ((tag >> 8) & 0xFF); + d = (uint8_t) (tag & 0xFF); + // TODO handle return value + (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { + struct otfForEach ofe; + + memset(&ofe, 0, sizeof (struct otfForEach)); + ofe.f = f; + ofe.data = data; + g_hash_table_foreach(otf->tags, foreach, &ofe); +} + +static gint tagcmp(gpointer a, gpointer b) +{ + return GINT_FROM_POINTER(a) - GINT_FROM_POINTER(b); +} + +static GList *copySortedKeys(GHashTable *tags) +{ + GList *k, *copy; + + k = g_hash_table_get_keys(tags); + copy = g_list_copy(k); + copy = g_list_sort(copy, tagcmp); + // TODO do we free k? the docs contradict themselves + return copy; } int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { + GList *ak, *bk; + GList *ai, *bi; + guint na, nb; + guint i; + int equal = 0; + + ak = copySortedKeys(a); + bk = copySortedKeys(b); + + na = g_list_length(ak); + nb = g_list_length(bk); + if (na != nb) { + equal = 0; + goto out; + } + + // TODO use GINT_FROM_POINTER() in these? + ai = ak; + bi = bk; + for (i = 0; i < na; i++) { + gpointer av, bv; + + // compare keys + // this is why we needed to sort earlier + if (ai->data != bi->data) { + equal = 0; + goto out; + } + // and compare values + av = g_hash_table_lookup(a, ai->data); + bv = g_hash_table_lookup(b, bi->data); + if (av != bv) { + equal = 0; + goto out; + } + ai = ai->next; + bi = bi->next; + } + + // all good + equal = 1; + +out: + g_list_free(bk); + g_list_free(ak); + return equal; } -// TODO put the internal function to produce a backend object from one here +// TODO make this a g_hash_table_foreach() function (which requires duplicating code)? +static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) +{ + // TODO is there a G_STRING()? + GString *s = (GString *) data; + + // the last trailing comma is removed after foreach is done + g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", + a, b, c, d, value); + // TODO use this + return 0; +} + +GString *otfToPangoCSSString(uiOpenTypeFeatures *otf) +{ + GString *s; + + s = g_string_new(""); + uiOpenTypeFeaturesForEach(otf, toCSS, s); + if (s->len != 0) + // and remove the last comma + g_string_truncate(s, s->len - 2); + return s; +} From 39cec570d93d59c68ffc34ea2d5d5d56cc10b604 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 23:37:16 -0400 Subject: [PATCH 220/487] And implemented the new features stuff on the GTK+ side. --- unix/attrstr.c | 58 +++++++++++----------------------------------- unix/opentype.c | 19 ++++++++------- unix/uipriv_unix.h | 3 +++ 3 files changed, 27 insertions(+), 53 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 88eabef4..7bf556c9 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -10,7 +10,7 @@ struct foreachParams { const char *s; PangoAttrList *attrs; // keys are pointers to size_t maintained by g_new0()/g_free() - // values are GStrings + // values are strings GHashTable *features; // TODO use pango's built-in background attribute? GPtrArray *backgroundClosures; @@ -38,24 +38,22 @@ static void freeFeatureString(gpointer s) #define isCodepointStart(b) (((uint8_t) (b)) <= 0x7F || ((uint8_t) (b)) >= 0xC0) -static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) { size_t i; size_t *key; GString *new; + new = otfToPangoCSSString(otf); for (i = start; i < end; i++) { // don't create redundant entries; see below if (!isCodepointStart(p->s[i])) continue; - new = (GString *) g_hash_table_lookup(p->features, &i); - if (new != NULL) - continue; - new = g_string_new(""); key = g_new0(size_t, 1); *key = i; - g_hash_table_replace(p->features, key, new); + g_hash_table_replace(p->features, key, g_strdup(new->str)); } + g_string_free(new, TRUE); } struct closureParams { @@ -104,30 +102,6 @@ static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double return closure; } -struct otParam { - struct foreachParams *p; - size_t start; - size_t end; -}; - -// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings -static void doOpenType(const char *featureTag, uint32_t param, void *data) -{ - struct otParam *p = (struct otParam *) data; - size_t i; - GString *s; - - ensureFeaturesInRange(p->p, p->start, p->end); - for (i = p->start; i < p->end; i++) { - // don't use redundant entries; see below - if (!isCodepointStart(p->p->s[i])) - continue; - s = (GString *) g_hash_table_lookup(p->p->features, &i); - g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", - featureTag, param); - } -} - static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) { if (attr == NULL) // in case of a future attribute @@ -142,7 +116,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; PangoUnderline underline; - struct otParam op; switch (spec->Type) { case uiAttributeFamily: @@ -225,14 +198,13 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - default: - // handle typographic features - op.p = p; - op.start = start; - op.end = end; - // TODO check if unhandled and complain - specToOpenType(spec, doOpenType, &op); + case uiAttributeFeatures: + // TODO ensure the parentheses around spec->Value are provided on Windows + setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); break; + default: + // TODO complain + ; } return 0; } @@ -241,18 +213,16 @@ static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) { struct foreachParams *p = (struct foreachParams *) data; size_t *pos = (size_t *) key; - GString *s = (GString *) value; + char *s = (char *) value; size_t n; - // remove the trailing comma/space - g_string_truncate(s, s->len - 2); // make sure we cover an entire code point // otherwise Pango will break apart multi-byte characters, spitting out U+FFFD characters at the invalid points n = 1; while (!isCodepointStart(p->s[*pos + n])) n++; addattr(p, *pos, *pos + n, - FUTURE_pango_attr_font_features_new(s->str)); + FUTURE_pango_attr_font_features_new(s)); return TRUE; // always delete; we're emptying the map } @@ -275,7 +245,7 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **bac fep.attrs = pango_attr_list_new(); fep.features = g_hash_table_new_full( featurePosHash, featurePosEqual, - g_free, freeFeatureString); + g_free, g_free); fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFeatureAttributes(&fep); diff --git a/unix/opentype.c b/unix/opentype.c index 12869a6a..6ea317c5 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -1,7 +1,7 @@ // 11 may 2017 -#include "uipriv_windows.hpp" +#include "uipriv_unix.h" -// TODO switch from GINT_FROM_POINTER() and so to a fake GUINT_FROM_POINTER()? +// TODO switch from GINT_TO_POINTER() and so to a fake GUINT_TO_POINTER()? struct uiOpenTypeFeatures { GHashTable *tags; @@ -106,9 +106,9 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac g_hash_table_foreach(otf->tags, foreach, &ofe); } -static gint tagcmp(gpointer a, gpointer b) +static gint tagcmp(gconstpointer a, gconstpointer b) { - return GINT_FROM_POINTER(a) - GINT_FROM_POINTER(b); + return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b); } static GList *copySortedKeys(GHashTable *tags) @@ -130,8 +130,8 @@ int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) guint i; int equal = 0; - ak = copySortedKeys(a); - bk = copySortedKeys(b); + ak = copySortedKeys(a->tags); + bk = copySortedKeys(b->tags); na = g_list_length(ak); nb = g_list_length(bk); @@ -140,7 +140,7 @@ int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) goto out; } - // TODO use GINT_FROM_POINTER() in these? + // TODO use GPOINTER_TO_INT() in these? ai = ak; bi = bk; for (i = 0; i < na; i++) { @@ -153,8 +153,8 @@ int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) goto out; } // and compare values - av = g_hash_table_lookup(a, ai->data); - bv = g_hash_table_lookup(b, bi->data); + av = g_hash_table_lookup(a->tags, ai->data); + bv = g_hash_table_lookup(b->tags, bi->data); if (av != bv) { equal = 0; goto out; @@ -172,6 +172,7 @@ out: return equal; } +// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings // TODO make this a g_hash_table_foreach() function (which requires duplicating code)? static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) { diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index bde6502f..cb5bf281 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -71,3 +71,6 @@ extern void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawT #define cairoToPango(cairo) (pango_units_from_double(cairo)) extern const PangoStyle pangoItalics[]; extern const PangoStretch pangoStretches[]; + +// opentype.c +extern GString *otfToPangoCSSString(uiOpenTypeFeatures *otf); From 72f5b680f23e8dde26e0795af868f53fda2a1d2d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 14:12:04 -0400 Subject: [PATCH 221/487] Filled in darwin/opentype.m. --- darwin/CMakeLists.txt | 1 + darwin/opentype.m | 58 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 96db9c09..e260f266 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -31,6 +31,7 @@ list(APPEND _LIBUI_SOURCES darwin/map.m darwin/menu.m darwin/multilineentry.m + darwin/opentype.m darwin/progressbar.m darwin/radiobuttons.m darwin/scrollview.m diff --git a/darwin/opentype.m b/darwin/opentype.m index 4273b80f..c8158356 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -1,8 +1,8 @@ // 11 may 2017 -#include "uipriv_windows.hpp" +#import "uipriv_darwin.h" struct uiOpenTypeFeatures { - xxxx; + NSMutableDictionary *tags; }; uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) @@ -10,13 +10,13 @@ uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) uiOpenTypeFeatures *otf; otf = uiNew(uiOpenTypeFeatures); - xxxx; + otf->tags = [NSMutableDictionary new]; return otf; } void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) { - xxxxxx; + [otf->tags release]; uiFree(otf); } @@ -25,28 +25,74 @@ uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) uiOpenTypeFeatures *out; out = uiNew(uiOpenTypeFeatures); - xxxxxx; + out->tags = [otf->tags mutableCopy]; return out; } +// TODO provide to aat.m too; remove x8tox32() when doing so +#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) + +// why are there no NSNumber methods for stdint.h or the equivalent core foundation types?... +#define mkMapObject(tag) [NSNumber numberWithUnsignedLongLong:((unsigned long long) tag)] +#define mapObjectValue(num) ((uint32_t) [num unsignedLongLongValue]) + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { + NSNumber *tn, *vn; + + tn = mkMapObject(mkTag(a, b, c, d)); + vn = mkMapObject(value); + [otf->tags setObject:vn forKey:tn]; } void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + NSNumber *tn; + + tn = mkMapObject(mkTag(a, b, c, d)); + [otf->tags removeObjectForKey:tn]; } int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { + NSNumber *tn, *vn; + + tn = mkMapObject(mkTag(a, b, c, d)); + vn = (NSNumber *) [otf->tags objectForKey:tn]; + if (vn == nil) + return 0; + *value = mapObjectValue(vn); + // TODO release vn? + return 1; } void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { + [otf->tags enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { + NSNumber *tn = (NSNumber *) key; + NSNumber *vn = (NSNumber *) value; + uint32_t tag; + uint8_t a, b, c, d; + + tag = mapObjectValue(tn); + a = (uint8_t) ((tag >> 24) & 0xFF); + b = (uint8_t) ((tag >> 16) & 0xFF); + c = (uint8_t) ((tag >> 8) & 0xFF); + d = (uint8_t) (tag & 0xFF); + // TODO handle return value + (*f)((char) a, (char) b, (char) c, (char) d, + mapObjectValue(vn), data); + }]; } int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { + return [a->tags isEqualToDictionary:b->tags]; } -// TODO put the internal function to produce a backend object from one here +// actual conversion to a feature dictionary is handled in aat.m; see there for details From 1a2dd1f16bd747f357e85d62cfdf6706d16e32bc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 15:14:41 -0400 Subject: [PATCH 222/487] Converted darwin/aat.m to the new OpenType system. We can't use the code as-is just yet, though. --- darwin/aat.m | 583 ++++++++++++++++++++++++++------------------------- 1 file changed, 296 insertions(+), 287 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index c7fc42e1..84b13c83 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,388 +1,397 @@ // 14 february 2017 #import "uipriv_darwin.h" -static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, specToAATEnumFunc f, void *data) +struct openTypeAATParams { + void (*doAAT)(uint16_t type, uint16_t selector, void *data); + void *data; +}; + +#define pcall(p, type, selector) ((*(p->doAAT))(type, selector, p->data)) + +static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, struct openTypeAATParams *p) { - if (spec->Value != 0) { - (*f)(type, ifTrue, data); + // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) + if (value != 0) { + pcall(p, type, ifTrue); return; } - (*f)(type, ifFalse, data); + pcall(p, type, ifFalse); } -int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) +void openTypeToAAT(char a, char b, char c, char d, uint32_t value, void *data) { - switch (spec->Type) { - case uiAttributeStandardLigatures: - boolspec(spec, kLigaturesType, + struct openTypeAATParams *p = (struct openTypeAATParams *) data; + + switch (mkTag(a, b, c, d)) { + case mkTag('l', 'i', 'g', 'a'): + boolspec(value, kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector, - f, data); - return 1; - case uiAttributeRequiredLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('r', 'l', 'i', 'g'): + boolspec(value, kLigaturesType, kRequiredLigaturesOnSelector, kRequiredLigaturesOffSelector, - f, data); - return 1; - case uiAttributeDiscretionaryLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('d', 'l', 'i', 'g'): + boolspec(value, kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector, - f, data); - return 1; - case uiAttributeContextualLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('c', 'l', 'i', 'g'): + boolspec(value, kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector, - f, data); - return 1; - case uiAttributeHistoricalLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('h', 'l', 'i', 'g'): + // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too + case mkTag('h', 'i', 's', 't'): + boolspec(value, kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector, - f, data); - return 1; - case uiAttributeUnicase: + p); + break; + case mkTag('u', 'n', 'i', 'c'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) + if (value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table - (*f)(kLetterCaseType, 14, data); - return 1; - // TODO make an array? - case uiAttributeNumberSpacings: - switch (spec->Value) { - case uiAttributeNumberSpacingProportional: - (*f)(kNumberSpacingType, kProportionalNumbersSelector, data); - break; - case uiAttributeNumberSpacingTabular: - (*f)(kNumberSpacingType, kMonospacedNumbersSelector, data); - break; - } - return 1; - // TODO make an array? - case uiAttributeSuperscripts: - switch (spec->Value) { - case uiAttributeSuperscriptNone: - (*f)(kVerticalPositionType, kNormalPositionSelector, data); - break; - case uiAttributeSuperscriptSuperscript: - (*f)(kVerticalPositionType, kSuperiorsSelector, data); - break; - case uiAttributeSuperscriptSubscript: - (*f)(kVerticalPositionType, kInferiorsSelector, data); - break; - case uiAttributeSuperscriptOrdinal: - (*f)(kVerticalPositionType, kOrdinalsSelector, data); - break; - case uiAttributeSuperscriptScientificInferior: - (*f)(kVerticalPositionType, kScientificInferiorsSelector, data); - break; - } - return 1; - // TODO make an array? - case uiAttributeFractionForms: - switch (spec->Value) { - case uiAttributeFractionFormNone: - (*f)(kFractionsType, kNoFractionsSelector, data); - break; - case uiAttributeFractionFormVertical: - (*f)(kFractionsType, kVerticalFractionsSelector, data); - break; - case uiAttributeFractionFormDiagonal: - (*f)(kFractionsType, kDiagonalFractionsSelector, data); - break; - } - return 1; - case uiAttributeSlashedZero: - boolspec(spec, kTypographicExtrasType, + pcall(p, kLetterCaseType, 14); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('p', 'n', 'u', 'm'): + if (value != 0) + pcall(p, kNumberSpacingType, kProportionalNumbersSelector); + break; + case mkTag('t', 'n', 'u', 'm'): + if (value != 0) + pcall(p, kNumberSpacingType, kMonospacedNumbersSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('s', 'u', 'p', 's'): + if (value != 0) + pcall(p, kVerticalPositionType, kSuperiorsSelector); + break; + case mkTag('s', 'u', 'b', 's'): + if (value != 0) + pcall(p, kVerticalPositionType, kInferiorsSelector); + break; + case mkTag('o', 'r', 'd', 'n'): + if (value != 0) + pcall(p, kVerticalPositionType, kOrdinalsSelector); + break; + case mkTag('s', 'i', 'n', 'f'): + if (value != 0) + pcall(p, kVerticalPositionType, kScientificInferiorsSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('a', 'f', 'r', 'c'): + if (value != 0) + pcall(p, kFractionsType, kVerticalFractionsSelector); + break; + case mkTag('f', 'r', 'a', 'c'): + if (value != 0) + pcall(p, kFractionsType, kDiagonalFractionsSelector); + break; + + case mkTag('z', 'e', 'r', 'o'): + boolspec(value, kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector, - f, data); - return 1; - case uiAttributeMathematicalGreek: - boolspec(spec, kMathematicalExtrasType, + p); + break; + case mkTag('m', 'g', 'r', 'k'): + boolspec(value, kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector, - f, data); - return 1; - case uiAttributeOrnamentalForms: - (*f)(kOrnamentSetsType, (uint16_t) (spec->Value), data); - return 1; - case uiAttributeSpecificCharacterForm: - (*f)(kCharacterAlternativesType, (uint16_t) (spec->Value), data); - return 1; - case uiAttributeTitlingCapitalForms: + p); + break; + case mkTag('o', 'r', 'n', 'm'): + pcall(p, kOrnamentSetsType, (uint16_t) value); + break; + case mkTag('a', 'a', 'l', 't'): + pcall(p, kCharacterAlternativesType, (uint16_t) value); + break; + case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? if (spec->Value != 0) - (*f)(kStyleOptionsType, kTitlingCapsSelector, data); - return 1; - // TODO make an array? - case uiAttributeHanCharacterForms: - switch (spec->Value) { - case uiAttributeHanCharacterFormTraditional: - (*f)(kCharacterShapeType, kTraditionalCharactersSelector, data); - break; - case uiAttributeHanCharacterFormSimplified: - (*f)(kCharacterShapeType, kSimplifiedCharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS1978: - (*f)(kCharacterShapeType, kJIS1978CharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS1983: - (*f)(kCharacterShapeType, kJIS1983CharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS1990: - (*f)(kCharacterShapeType, kJIS1990CharactersSelector, data); - break; - case uiAttributeHanCharacterFormExpert: - (*f)(kCharacterShapeType, kExpertCharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS2004: - (*f)(kCharacterShapeType, kJIS2004CharactersSelector, data); - break; - case uiAttributeHanCharacterFormHojo: - (*f)(kCharacterShapeType, kHojoCharactersSelector, data); - break; - case uiAttributeHanCharacterFormNLC: - (*f)(kCharacterShapeType, kNLCCharactersSelector, data); - break; - case uiAttributeHanCharacterFormTraditionalNames: - (*f)(kCharacterShapeType, kTraditionalNamesCharactersSelector, data); - break; - } - return 1; - case uiAttributeLowercaseNumbers: + pcall(p, kStyleOptionsType, kTitlingCapsSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('t', 'r', 'a', 'd'): + if (value != 0) + pcall(p, kCharacterShapeType, kTraditionalCharactersSelector); + break; + case mkTag('s', 'm', 'p', 'l'): + if (value != 0) + pcall(p, kCharacterShapeType, kSimplifiedCharactersSelector); + break; + case mkTag('j', 'p', '7', '8'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS1978CharactersSelector); + break; + case mkTag('j', 'p', '8', '3'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS1983CharactersSelector); + break; + case mkTag('j', 'p', '9', '0'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS1990CharactersSelector); + break; + case mkTag('e', 'x', 'p', 't'): + if (value != 0) + pcall(p, kCharacterShapeType, kExpertCharactersSelector); + break; + case mkTag('j', 'p', '0', '4'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS2004CharactersSelector); + break; + case mkTag('h', 'o', 'j', 'o'): + if (value != 0) + pcall(p, kCharacterShapeType, kHojoCharactersSelector); + break; + case mkTag('n', 'l', 'c', 'k'): + if (value != 0) + pcall(p, kCharacterShapeType, kNLCCharactersSelector); + break; + case mkTag('t', 'n', 'a', 'm'): + if (value != 0) + pcall(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); + break; + + case mkTag('o', 'n', 'u', 'm'): + // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too + // TODO is it always set? + case mkTag('l', 'n', 'u', 'm'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) - (*f)(kNumberCaseType, kLowerCaseNumbersSelector, data); - return 1; - case uiAttributeHanjaToHangul: + if (value != 0) + pcall(p, kNumberCaseType, kLowerCaseNumbersSelector); + break; + case mkTag('h', 'n', 'g', 'l'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) - (*f)(kTransliterationType, kHanjaToHangulSelector, data); - return 1; - case uiAttributeAnnotatedGlyphForms: - (*f)(kAnnotationType, (uint16_t) (spec->Value), data); - return 1; - case uiAttributeRubyKanaForms: + if (value != 0) + pcall(p, kTransliterationType, kHanjaToHangulSelector); + break; + case mkTag('n', 'a', 'l', 't'): + pcall(p, kAnnotationType, (uint16_t) value); + break; + case mkTag('r', 'u', 'b', 'y'): // include this for completeness - boolspec(spec, kRubyKanaType, + boolspec(value, kRubyKanaType, kRubyKanaSelector, kNoRubyKanaSelector, - f, data); + p); // this is the current one - boolspec(spec, kRubyKanaType, + boolspec(value, kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector, - f, data); - return 1; - case uiAttributeCJKRomansToItalics: + p); + break; + case mkTag('i', 't', 'a', 'l'): // include this for completeness - boolspec(spec, kItalicCJKRomanType, + boolspec(value, kItalicCJKRomanType, kCJKItalicRomanSelector, kNoCJKItalicRomanSelector, - f, data); + p); // this is the current one - boolspec(spec, kItalicCJKRomanType, + boolspec(value, kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector, - f, data); - return 1; - case uiAttributeCaseSensitiveForms: - boolspec(spec, kCaseSensitiveLayoutType, + p); + break; + case mkTag('c', 'a', 's', 'e'): + boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector, - f, data); - return 1; - case uiAttributeCapitalSpacing: - boolspec(spec, kCaseSensitiveLayoutType, + p); + break; + case mkTag('c', 'p', 's', 'p'): + boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector, - f, data); - return 1; - case uiAttributeAlternateHorizontalKana: - boolspec(spec, kAlternateKanaType, + p); + break; + case mkTag('h', 'k', 'n', 'a'): + boolspec(value, kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, - f, data); - return 1; - case uiAttributeAlternateVerticalKana: - boolspec(spec, kAlternateKanaType, + p); + break; + case mkTag('v', 'k', 'n', 'a'): + boolspec(value, kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate1: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '1'): + boolspec(value, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate2: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '2'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate3: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '3'): + boolspec(value, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate4: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '4'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate5: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '5'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate6: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '6'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate7: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '7'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate8: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '8'): + boolspec(value, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate9: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '9'): + boolspec(value, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate10: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '0'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate11: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '1'): + boolspec(value, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate12: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '2'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate13: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '3'): + boolspec(value, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate14: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '4'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate15: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '5'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate16: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '6'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate17: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '7'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate18: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '8'): + boolspec(value, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate19: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '9'): + boolspec(value, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate20: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '2', '0'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, - f, data); - return 1; - case uiAttributeContextualAlternates: - boolspec(spec, kContextualAlternatesType, + p); + break; + case mkTag('c', 'a', 'l', 't'): + boolspec(value, kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector, - f, data); - return 1; - case uiAttributeSwashes: - boolspec(spec, kContextualAlternatesType, + p); + break; + case mkTag('s', 'w', 's', 'h'): + boolspec(value, kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector, - f, data); - return 1; - case uiAttributeContextualSwashes: - boolspec(spec, kContextualAlternatesType, + p); + break; + case mkTag('c', 's', 'w', 'h'): + boolspec(value, kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector, - f, data); - return 1; - // TODO use arrays? - case uiAttributeLowercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormNormal: - (*f)(kLowerCaseType, kDefaultLowerCaseSelector, data); - break; - case uiAttributeCapFormSmallCaps: + p); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('s', 'm', 'c', 'p'): + if (value != 0) { // include this for compatibility (some fonts that come with OS X still use this!) // TODO make it boolean? - (*f)(kLetterCaseType, kSmallCapsSelector, data); + pcall(p, kLetterCaseType, kSmallCapsSelector); // this is the current one - (*f)(kLowerCaseType, kLowerCaseSmallCapsSelector, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)(kLowerCaseType, kLowerCasePetiteCapsSelector, data); - break; + pcall(p, kLowerCaseType, kLowerCaseSmallCapsSelector); } - return 1; - // TODO use arrays? - case uiAttributeUppercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormNormal: - (*f)(kUpperCaseType, kDefaultUpperCaseSelector, data); - break; - case uiAttributeCapFormSmallCaps: - (*f)(kUpperCaseType, kUpperCaseSmallCapsSelector, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)(kUpperCaseType, kUpperCasePetiteCapsSelector, data); - break; - } - return 1; + break; + case mkTag('p', 'c', 'a', 'p'): + if (value != 0) + pcall(p, kLowerCaseType, kLowerCasePetiteCapsSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('c', '2', 's', 'c'): + if (value != 0) + pcall(p, kUpperCaseType, kUpperCaseSmallCapsSelector); + break; + case mkTag('c', '2', 'p', 'c'): + if (value != 0) + pcall(p, kUpperCaseType, kUpperCasePetiteCapsSelector); + break; } - return 0; } From b7d34bf4f5d4664960feeeb48215eba95a6b2a04 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 15:48:15 -0400 Subject: [PATCH 223/487] And tied everyhting together, sort of. --- darwin/aat.m | 13 ++++++++++++- darwin/attrstr.m | 10 ++++++---- darwin/opentype.m | 8 -------- darwin/uipriv_darwin.h | 13 +++++++++++-- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index 84b13c83..45a97b00 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -18,7 +18,7 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if pcall(p, type, ifFalse); } -void openTypeToAAT(char a, char b, char c, char d, uint32_t value, void *data) +static int foreach(char a, char b, char c, char d, uint32_t value, void *data) { struct openTypeAATParams *p = (struct openTypeAATParams *) data; @@ -394,4 +394,15 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, void *data) pcall(p, kUpperCaseType, kUpperCasePetiteCapsSelector); break; } + // TODO handle this properly + return 0; +} + +void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data) +{ + struct openTypeAATParams p; + + p.doAAT = doAAT; + p.data = data; + uiOpenTypeFeaturesForEach(otf, foreach, &p); } diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 3e5797b4..556b09ad 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -155,6 +155,7 @@ static void doAAT(uint16_t type, uint16_t selector, void *data) fp->nFeatures++; if (fp->nFeatures == maxFeatures) { // TODO + // TODO move this check to the top like in the drawtext example? and all the other instances of this? } }); } @@ -257,14 +258,15 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; - default: - // handle typographic features + case uiAttributeFeatures: ap.p = p; ap.start = start; ap.end = end; - // TODO check if unhandled and complain - specToAAT(spec, doAAT, &ap); + openTypeToAAT((uiOpenTypeFeatures *) (spec->Value), doAAT, &ap); break; + default: + // TODO complain + ; } return 0; } diff --git a/darwin/opentype.m b/darwin/opentype.m index c8158356..25939299 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -29,14 +29,6 @@ uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) return out; } -// TODO provide to aat.m too; remove x8tox32() when doing so -#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) -#define mkTag(a, b, c, d) \ - ((x8tox32(a) << 24) | \ - (x8tox32(b) << 16) | \ - (x8tox32(c) << 8) | \ - x8tox32(d)) - // why are there no NSNumber methods for stdint.h or the equivalent core foundation types?... #define mkMapObject(tag) [NSNumber numberWithUnsignedLongLong:((unsigned long long) tag)] #define mapObjectValue(num) ((uint32_t) [num unsignedLongLongValue]) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c3de53e2..8c4bff07 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -152,5 +152,14 @@ typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, doub extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); // aat.m -typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); -extern int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data); +extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data); + +// opentype.m +// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this +// TODO remove x8tox32() +#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) From bd39189a0e7820208b2a27c5cef566ceaa7c9ab5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:10:54 -0400 Subject: [PATCH 224/487] Fixed the build and an unspotted error in the drawtext example. --- darwin/aat.m | 3 ++- darwin/uipriv_darwin.h | 2 +- examples/drawtext/attributes.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index 45a97b00..0c964e05 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -18,6 +18,7 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if pcall(p, type, ifFalse); } +// TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) static int foreach(char a, char b, char c, char d, uint32_t value, void *data) { struct openTypeAATParams *p = (struct openTypeAATParams *) data; @@ -120,7 +121,7 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) break; case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) + if (value != 0) pcall(p, kStyleOptionsType, kTitlingCapsSelector); break; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 8c4bff07..fe65f201 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -157,7 +157,7 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, // opentype.m // TODO this is only used by opentype.m and aat.m; figure out some better way to handle this // TODO remove x8tox32() -#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) #define mkTag(a, b, c, d) \ ((x8tox32(a) << 24) | \ (x8tox32(b) << 16) | \ diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 56ed4d13..46bf1b0d 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -218,7 +218,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("clig", 1); + spec.Value = addFeature("dlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); From 90962e18c4e01ad1f8c9f927b1a911ed09e00773 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:14:39 -0400 Subject: [PATCH 225/487] And got rid of the remaining old stuff entirely. --- common/opentype.c | 314 ---------------------------------------------- ui_attrstr.h | 169 ------------------------- 2 files changed, 483 deletions(-) delete mode 100644 common/opentype.c diff --git a/common/opentype.c b/common/opentype.c deleted file mode 100644 index 3b2ddf64..00000000 --- a/common/opentype.c +++ /dev/null @@ -1,314 +0,0 @@ -// 14 february 2017 -#include "../ui.h" -#include "uipriv.h" - -// Notes: -// - Each tag should only appear in quotes once (including within comments); this allows automated tools to determine what we cover and don't cover - -static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) -{ - if (spec->Value != 0) { - (*f)(featureTag, 1, data); - return; - } - (*f)(featureTag, 0, data); -} - -static void boolspecnot(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) -{ - if (spec->Value == 0) { - (*f)(featureTag, 1, data); - return; - } - (*f)(featureTag, 0, data); -} - -void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) -{ - switch (spec->Type) { - case uiAttributeStandardLigatures: - boolspec(spec, "liga", f, data); - return; - case uiAttributeRequiredLigatures: - boolspec(spec, "rlig", f, data); - return; - case uiAttributeDiscretionaryLigatures: - boolspec(spec, "dlig", f, data); - return; - case uiAttributeContextualLigatures: - boolspec(spec, "clig", f, data); - return; - case uiAttributeHistoricalLigatures: - boolspec(spec, "hlig", f, data); - // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too - boolspec(spec, "hist", f, data); - return; - case uiAttributeUnicase: - boolspec(spec, "unic", f, data); - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeNumberSpacings: - // TODO does Core Text set both? do we? - switch (spec->Value) { - case uiAttributeNumberSpacingProportional: - (*f)("pnum", 1, data); - break; - case uiAttributeNumberSpacingTabular: - (*f)("tnum", 1, data); - break; - } - return; - case uiAttributeSuperscripts: - switch (spec->Value) { - case uiAttributeSuperscriptSuperscript: - (*f)("sups", 1, data); - break; - case uiAttributeSuperscriptSubscript: - (*f)("subs", 1, data); - break; - case uiAttributeSuperscriptOrdinal: - (*f)("ordn", 1, data); - break; - case uiAttributeSuperscriptScientificInferior: - (*f)("sinf", 1, data); - break; - } - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeFractionForms: - switch (spec->Value) { - case uiAttributeFractionFormVertical: - (*f)("afrc", 1, data); - break; - case uiAttributeFractionFormDiagonal: - (*f)("frac", 1, data); - break; - } - return; - case uiAttributeSlashedZero: - boolspec(spec, "zero", f, data); - return; - case uiAttributeMathematicalGreek: - boolspec(spec, "mgrk", f, data); - return; - case uiAttributeOrnamentalForms: - (*f)("ornm", (uint32_t) (spec->Value), data); - return; - case uiAttributeSpecificCharacterForm: - (*f)("aalt", (uint32_t) (spec->Value), data); - return; - case uiAttributeTitlingCapitalForms: - boolspec(spec, "titl", f, data); - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeHanCharacterForms: - switch (spec->Value) { - case uiAttributeHanCharacterFormTraditional: - (*f)("trad", 1, data); - break; - case uiAttributeHanCharacterFormSimplified: - (*f)("smpl", 1, data); - break; - case uiAttributeHanCharacterFormJIS1978: - (*f)("jp78", 1, data); - break; - case uiAttributeHanCharacterFormJIS1983: - (*f)("jp83", 1, data); - break; - case uiAttributeHanCharacterFormJIS1990: - (*f)("jp90", 1, data); - break; - case uiAttributeHanCharacterFormExpert: - (*f)("expt", 1, data); - break; - case uiAttributeHanCharacterFormJIS2004: - (*f)("jp04", 1, data); - break; - case uiAttributeHanCharacterFormHojo: - (*f)("hojo", 1, data); - break; - case uiAttributeHanCharacterFormNLC: - (*f)("nlck", 1, data); - break; - case uiAttributeHanCharacterFormTraditionalNames: - (*f)("tnam", 1, data); - break; - } - return; - case uiAttributeLowercaseNumbers: - boolspec(spec, "onum", f, data); - // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too - // TODO is it always set? - boolspecnot(spec, "lnum", f, data); - return; - case uiAttributeHanjaToHangul: - boolspec(spec, "hngl", f, data); - return; - case uiAttributeAnnotatedGlyphForms: - (*f)("nalt", (uint32_t) (spec->Value), data); - return; - case uiAttributeRubyKanaForms: - boolspec(spec, "ruby", f, data); - return; - case uiAttributeCJKRomansToItalics: - boolspec(spec, "ital", f, data); - return; - case uiAttributeCaseSensitiveForms: - boolspec(spec, "case", f, data); - return; - case uiAttributeCapitalSpacing: - boolspec(spec, "cpsp", f, data); - return; - case uiAttributeAlternateHorizontalKana: - boolspec(spec, "hkna", f, data); - return; - case uiAttributeAlternateVerticalKana: - boolspec(spec, "vkna", f, data); - return; - case uiAttributeStylisticAlternate1: - boolspec(spec, "ss01", f, data); - return; - case uiAttributeStylisticAlternate2: - boolspec(spec, "ss02", f, data); - return; - case uiAttributeStylisticAlternate3: - boolspec(spec, "ss03", f, data); - return; - case uiAttributeStylisticAlternate4: - boolspec(spec, "ss04", f, data); - return; - case uiAttributeStylisticAlternate5: - boolspec(spec, "ss05", f, data); - return; - case uiAttributeStylisticAlternate6: - boolspec(spec, "ss06", f, data); - return; - case uiAttributeStylisticAlternate7: - boolspec(spec, "ss07", f, data); - return; - case uiAttributeStylisticAlternate8: - boolspec(spec, "ss08", f, data); - return; - case uiAttributeStylisticAlternate9: - boolspec(spec, "ss09", f, data); - return; - case uiAttributeStylisticAlternate10: - boolspec(spec, "ss10", f, data); - return; - case uiAttributeStylisticAlternate11: - boolspec(spec, "ss11", f, data); - return; - case uiAttributeStylisticAlternate12: - boolspec(spec, "ss12", f, data); - return; - case uiAttributeStylisticAlternate13: - boolspec(spec, "ss13", f, data); - return; - case uiAttributeStylisticAlternate14: - boolspec(spec, "ss14", f, data); - return; - case uiAttributeStylisticAlternate15: - boolspec(spec, "ss15", f, data); - return; - case uiAttributeStylisticAlternate16: - boolspec(spec, "ss16", f, data); - return; - case uiAttributeStylisticAlternate17: - boolspec(spec, "ss17", f, data); - return; - case uiAttributeStylisticAlternate18: - boolspec(spec, "ss18", f, data); - return; - case uiAttributeStylisticAlternate19: - boolspec(spec, "ss19", f, data); - return; - case uiAttributeStylisticAlternate20: - boolspec(spec, "ss20", f, data); - return; - case uiAttributeContextualAlternates: - boolspec(spec, "calt", f, data); - return; - case uiAttributeSwashes: - boolspec(spec, "swsh", f, data); - return; - case uiAttributeContextualSwashes: - boolspec(spec, "cswh", f, data); - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeLowercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormSmallCaps: - (*f)("smcp", 1, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)("pcap", 1, data); - break; - } - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeUppercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormSmallCaps: - (*f)("c2sc", 1, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)("c2pc", 1, data); - break; - } - return; - } -} - -// TODO missing that AAT uses directly: -// - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) -// missing that AAT knows about: -// - ccmp (compositions) -// - dnom, numr (fraction parts) — no AAT equivalent... -// - falt, jalt (Arabic support) -// - rclt (required contextual alternates) -// - lfbd, opbd, rtbd (optical bounds support) -// - locl (Cyrillic support) -// - ltra, ltrm, rtla, rtlm (bidi support) -// - mark, mkmk (mark positioning) -// - rand (random glyph selection candidates) -// - salt (stylistic alternatives) -// - size (sizing info) -// -// script-specific; core text and pango/harfbuzz use these automatically based on the language -// TODO if DirectWrite does too we can ignore them and just provide a language attribute (they all use BCP 47 syntax for language names) -// Tag Core Text? Harfbuzz? -// abvf yes yes -// abvm yes yes -// abvs yes TODO -// akhn yes yes -// blwf yes yes -// blwm yes yes -// blws yes TODO -// cjct yes yes -// curs yes yes -// dist yes yes -// falt TODO TODO -// fin2 yes yes -// fin3 yes yes -// fina yes yes -// half yes yes -// haln yes yes -// init yes yes -// isol yes yes -// jalt TODO TODO -// ljmo yes yes -// locl TODO all horz(!) -// med2 yes yes -// medi yes yes -// mset TODO yes -// nukt yes yes -// pref yes yes -// pres yes yes -// pstf yes yes -// psts yes yes -// rclt TODO all horz(!) -// rkrf yes yes -// rphf yes yes -// tjmo yes yes -// vatu yes yes -// vjmo yes yes diff --git a/ui_attrstr.h b/ui_attrstr.h index d66f2e9a..4d13acb7 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -34,132 +34,6 @@ _UI_ENUM(uiAttribute) { // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout uiAttributeFeatures, // object of type uiOpenTypeFeatures - -#if 0 - - // These attributes represent typographic features. Each feature - // is a separate attribute, to make composition easier. The - // availability of for each attribute are defined by the font; the - // default values are defined by the font and/or by the OS. - // - // A note about features whose parameter is an enumeration: - // OS X defines typographic features using the AAT specification - // and converts to OpenType internally when needed, whereas - // other platforms use OpenType directly. OpenType is less - // precise about what each enumeration value means than AAT - // is, so enumeration values do not necessarily represent what - // OS X expects with all fonts. In cases where they do, libui - // provides an enumeration type to use. Otherwise, the AAT - // enumeration values are provided in comments for - // documentation purposes. - - // AAT calls these "common ligatures" - uiAttributeStandardLigatures, // 0 = off, 1 = on - uiAttributeRequiredLigatures, // 0 = off, 1 = on - // AAT calls these "rare ligatures" - uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on - uiAttributeContextualLigatures, // 0 = off, 1 = on - uiAttributeHistoricalLigatures, // 0 = off, 1 = on - - uiAttributeUnicase, // 0 = off, 1 = on - - // TODO rename this - uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing - - uiAttributeSuperscripts, // enum uiAttributeSuperscript - - uiAttributeFractionForms, // enum uiAttributeFractionForm - - uiAttributeSlashedZero, // 0 = off, 1 = on - - uiAttributeMathematicalGreek, // 0 = off, 1 = on - - // AAT defines the following values: - // 0 = none - // 1 = dingbats - // 2 = pi characters - // 3 = fleurons - // 4 = decorative borders - // 5 = international symbols - // 6 = mathematical symbols - // OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many - uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound - // TODO provide a function to get the upper bound? - - // AAT calls this "character alternatives" and defines the - // following values: - // 0 = none - // OpenType calls this "access all alternates". - uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound - // TODO provide a function to get the upper bound? - - uiAttributeTitlingCapitalForms, // 0 = off, 1 = on - - // AAT calls these "character shapes" - uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm - - // OpenType calls these "old-style" - uiAttributeLowercaseNumbers, // 0 = off, 1 = on - - uiAttributeHanjaToHangul, // 0 = off, 1 = on - - // AAT defines the following values: - // 0 = none - // 1 = box - // 2 = rounded box - // 3 = circle - // 4 = inverted circle - // 5 = parentheses - // 6 = period - // 7 = roman numeral - // 8 = diamond - // 9 = inverted box - // 10 = inverted rounded box - uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound - // TODO provide a function to get the upper bound? - - uiAttributeRubyKanaForms, // 0 = off, 1 = on - - uiAttributeCJKRomansToItalics, // 0 = off, 1 = on - - // AAT calls this "case-sensitive layout" - uiAttributeCaseSensitiveForms, // 0 = off, 1 = on - // AAT: this is called "case-sensitive spacing" - uiAttributeCapitalSpacing, // 0 = off, 1 = on - - uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on - uiAttributeAlternateVerticalKana, // 0 = off, 1 = on - - // TODO document that these are guaranteed to be consecutive - uiAttributeStylisticAlternate1, // 0 = off, 1 = on - uiAttributeStylisticAlternate2, // 0 = off, 1 = on - uiAttributeStylisticAlternate3, // 0 = off, 1 = on - uiAttributeStylisticAlternate4, // 0 = off, 1 = on - uiAttributeStylisticAlternate5, // 0 = off, 1 = on - uiAttributeStylisticAlternate6, // 0 = off, 1 = on - uiAttributeStylisticAlternate7, // 0 = off, 1 = on - uiAttributeStylisticAlternate8, // 0 = off, 1 = on - uiAttributeStylisticAlternate9, // 0 = off, 1 = on - uiAttributeStylisticAlternate10, // 0 = off, 1 = on - uiAttributeStylisticAlternate11, // 0 = off, 1 = on - uiAttributeStylisticAlternate12, // 0 = off, 1 = on - uiAttributeStylisticAlternate13, // 0 = off, 1 = on - uiAttributeStylisticAlternate14, // 0 = off, 1 = on - uiAttributeStylisticAlternate15, // 0 = off, 1 = on - uiAttributeStylisticAlternate16, // 0 = off, 1 = on - uiAttributeStylisticAlternate17, // 0 = off, 1 = on - uiAttributeStylisticAlternate18, // 0 = off, 1 = on - uiAttributeStylisticAlternate19, // 0 = off, 1 = on - uiAttributeStylisticAlternate20, // 0 = off, 1 = on - - uiAttributeContextualAlternates, // 0 = off, 1 = on - uiAttributeSwashes, // 0 = off, 1 = on - uiAttributeContextualSwashes, // 0 = off, 1 = on - - uiAttributeLowercaseCapForms, // enum uiAttributeCapForm - uiAttributeUppercaseCapForms, // enum uiAttributeCapForm -}; -#endif }; _UI_ENUM(uiDrawUnderlineStyle) { @@ -176,49 +50,6 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; -#if 0 - -_UI_ENUM(uiAttributeNumberSpacing) { - uiAttributeNumberSpacingProportional, - // AAT calls this "monospaced" - uiAttributeNumberSpacingTabular, -}; - -_UI_ENUM(uiAttributeSuperscript) { - uiAttributeSuperscriptNone, - uiAttributeSuperscriptSuperscript, // AAT: "superior" - uiAttributeSuperscriptSubscript, // AAT: "inferior" - uiAttributeSuperscriptOrdinal, - uiAttributeSuperscriptScientificInferior, -}; - -_UI_ENUM(uiAttributeFractionForm) { - uiAttributeFractionFormNone, - uiAttributeFractionFormVertical, - uiAttributeFractionFormDiagonal, -}; - -_UI_ENUM(uiAttributeHanCharacterForm) { - uiAttributeHanCharacterFormTraditional, - uiAttributeHanCharacterFormSimplified, - uiAttributeHanCharacterFormJIS1978, - uiAttributeHanCharacterFormJIS1983, - uiAttributeHanCharacterFormJIS1990, - uiAttributeHanCharacterFormExpert, - uiAttributeHanCharacterFormJIS2004, - uiAttributeHanCharacterFormHojo, - uiAttributeHanCharacterFormNLC, - uiAttributeHanCharacterFormTraditionalNames, -}; - -_UI_ENUM(uiAttributeCapForm) { - uiAttributeCapFormNormal, - uiAttributeCapFormSmallCaps, - uiAttributeCapFormPetiteCaps, -}; - -#endif - // TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? From dd544696774a1bb620c44758cf050992429e09d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:40:52 -0400 Subject: [PATCH 226/487] Set up a future system for OS X like we have on GTK+ and moved everything we already have to it. You'll notice we also set up a loader for what we're going to use this for: using OpenType attributes directly on OS X. --- darwin/CMakeLists.txt | 1 + darwin/autolayout.m | 4 +--- darwin/future.m | 50 ++++++++++++++++++++++++++++++++++++++++++ darwin/main.m | 1 + darwin/uipriv_darwin.h | 8 +++++++ darwin/winmoveresize.m | 8 +++---- unix/uipriv_unix.h | 2 +- 7 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 darwin/future.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index e260f266..0591643b 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND _LIBUI_SOURCES darwin/fontbutton.m darwin/fontmatch.m darwin/form.m + darwin/future.m darwin/graphemes.m darwin/grid.m darwin/group.m diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 9964155f..3bf4acb7 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -12,9 +12,7 @@ NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRela attribute:attr2 multiplier:multiplier constant:c]; - // apparently only added in 10.9 - if ([constraint respondsToSelector:@selector(setIdentifier:)]) - [((id) constraint) setIdentifier:desc]; + FUTURE_NSLayoutConstraint_setIdentifier(constraint, desc); return constraint; } diff --git a/darwin/future.m b/darwin/future.m new file mode 100644 index 00000000..45208756 --- /dev/null +++ b/darwin/future.m @@ -0,0 +1,50 @@ +// 19 may 2017 +#import "uipriv_darwin.h" + +// functions and constants FROM THE FUTURE! + +// TODO add weight constants here? + +// added in OS X 10.10; we need 10.8 +CFStringRef FUTURE_kCTFontOpenTypeFeatureTag = NULL; +CFStringRef FUTURE_kCTFontOpenTypeFeatureValue = NULL; + +// note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) +void loadFutures(void) +{ + void *handle; + + // dlsym() walks the dependency chain, so opening the current process should be sufficient + handle = dlopen(NULL, RTLD_LAZY); + if (handle == NULL) + return; +#define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) + GET(FUTURE_kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureTag); + GET(FUTURE_kCTFontOpenTypeFeatureValue, kCTFontOpenTypeFeatureValue); + dlclose(handle); +} + +// wrappers for methods that exist in the future that we can check for with respondsToSelector: +// keep them in one place for convenience + +// apparently only added in 10.9; we need 10.8 +void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier) +{ + id cid = (id) constraint; + + if ([constraint respondsToSelector:@selector(setIdentifier:)]) + [cid setIdentifier:identifier]; +} + +// added in 10.11; we need 10.8 +// return whether this was done because we recreate its effects if not (see winmoveresize.m) +BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent) +{ + id cw = (id) w; + + if ([w respondsToSelector:@selector(performWindowDragWithEvent:)]) { + [cw performWindowDragWithEvent:initialEvent]; + return YES; + } + return NO; +} diff --git a/darwin/main.m b/darwin/main.m index 440343bc..60acecaf 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -119,6 +119,7 @@ const char *uiInit(uiInitOptions *o) [realNSApp() setDelegate:delegate]; initAlloc(); + loadFutures(); // always do this so we always have an application menu appDelegate().menuManager = [[menuManager new] autorelease]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index fe65f201..01594f48 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -2,6 +2,7 @@ #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8 #import +#include // see future.m #import "../ui.h" #import "../ui_darwin.h" #import "../common/uipriv.h" @@ -163,3 +164,10 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, (x8tox32(b) << 16) | \ (x8tox32(c) << 8) | \ x8tox32(d)) + +// future.m +extern CFStringRef FUTURE_kCTFontOpenTypeFeatureTag; +extern CFStringRef FUTURE_kCTFontOpenTypeFeatureValue; +extern void loadFutures(void); +extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); +extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 9145b7bb..2753b93b 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -48,12 +48,10 @@ void doManualMove(NSWindow *w, NSEvent *initialEvent) BOOL (^handleEvent)(NSEvent *e); __block BOOL done; - // this is only available on 10.11 and newer (LONGTERM FUTURE) - // but use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces - if ([w respondsToSelector:@selector(performWindowDragWithEvent:)]) { - [((id) w) performWindowDragWithEvent:initialEvent]; + // 10.11 gives us a method to handle this for us + // use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces + if (FUTURE_NSWindow_performWindowDragWithEvent(w, initialEvent)) return; - } mdp.w = w; mdp.initialFrame = [mdp.w frame]; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index cb5bf281..c80e8cc6 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -5,7 +5,7 @@ #define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_10 #include #include -#include // see drawtext.c +#include // see future.c #include #include #include From fb884abc416cc2e5bd3b46c183a339919b02d4de Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:58:15 -0400 Subject: [PATCH 227/487] Fixed memory issues with future.m. --- darwin/future.m | 5 +++-- darwin/uipriv_darwin.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/darwin/future.m b/darwin/future.m index 45208756..30b43bd6 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -4,10 +4,11 @@ // functions and constants FROM THE FUTURE! // TODO add weight constants here? +// TOOD explain why the constants need to be pointers themselves // added in OS X 10.10; we need 10.8 -CFStringRef FUTURE_kCTFontOpenTypeFeatureTag = NULL; -CFStringRef FUTURE_kCTFontOpenTypeFeatureValue = NULL; +CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; +CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) void loadFutures(void) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 01594f48..c22c82b6 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -166,8 +166,8 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, x8tox32(d)) // future.m -extern CFStringRef FUTURE_kCTFontOpenTypeFeatureTag; -extern CFStringRef FUTURE_kCTFontOpenTypeFeatureValue; +extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; +extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; extern void loadFutures(void); extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); From a17985b4cda285a747d31e450e48a24f5fa6c847 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 20:46:56 -0400 Subject: [PATCH 228/487] Some big TODOs I didn't realize until now. --- ui_attrstr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 4d13acb7..fc281f99 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,6 +19,7 @@ typedef struct uiAttributedString uiAttributedString; // Note: where you say "1 = on", any nonzero value means "on". (TODO) +// TODO ok, we need to figure out what to do about pointer objects: do we copy them or do we keep them safe? especially since we merge attributes... _UI_ENUM(uiAttribute) { uiAttributeFamily, uiAttributeSize, // use Double From 57873bae728f237ffdcb5717f00927b093c5f958 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 27 May 2017 18:19:08 -0400 Subject: [PATCH 229/487] Stuff. --- darwin/future.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/future.m b/darwin/future.m index 30b43bd6..a6988f83 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -2,9 +2,9 @@ #import "uipriv_darwin.h" // functions and constants FROM THE FUTURE! +// note: for constants, dlsym() returns the address of the constant itself, as if we had done &constantName // TODO add weight constants here? -// TOOD explain why the constants need to be pointers themselves // added in OS X 10.10; we need 10.8 CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; From 475ae4a4bf6287ba6eee94667a31fcbdd8b7c89c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 May 2017 00:19:49 -0400 Subject: [PATCH 230/487] Started making attribute manipulation better. In a sense. Pointers will be represented properly, and in a const-safe way. We'll need to make local copies of everything, of course. --- examples/drawtext/attributes.c | 124 ++++++++++++++++----------------- ui_attrstr.h | 10 +-- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 46bf1b0d..69bbe032 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -7,7 +7,7 @@ static uiAttributedString *attrstr; static uiOpenTypeFeatures *features[nFeatures]; static int curFeature = 0; -static uintptr_t addFeature(const char tag[4], uint32_t value) +static uiOpenTypeFeatures *addFeature(const char tag[4], uint32_t value) { uiOpenTypeFeatures *otf; @@ -19,7 +19,7 @@ static uintptr_t addFeature(const char tag[4], uint32_t value) uiOpenTypeFeaturesAdd(otf, tag[0], tag[1], tag[2], tag[3], value); features[curFeature] = otf; curFeature++; - return (uintptr_t) otf; + return otf; } // some of these examples come from Microsoft's and Apple's lists of typographic features and also https://www.fontfont.com/staticcontent/downloads/FF_OT_User_Guide.pdf @@ -38,7 +38,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFamily; - spec.Value = (uintptr_t) "Courier New"; + spec.Family = "Courier New"; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -170,7 +170,7 @@ static void setupAttributedString(void) spec.A = 0.75; uiAttributedStringSetAttribute(attrstr, &spec, start + 12, end); spec.Type = uiAttributeFamily; - spec.Value = (uintptr_t) "Helvetica"; + spec.Value = "Helvetica"; uiAttributedStringSetAttribute(attrstr, &spec, start + 8, end - 1); spec.Type = uiAttributeBackground; spec.R = 1.0; @@ -192,7 +192,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("liga", 1); + spec.Features = addFeature("liga", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -206,7 +206,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("rlig", 1); + spec.Features = addFeature("rlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "\xE2\x80\xAC)"); @@ -218,7 +218,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("dlig", 1); + spec.Features = addFeature("dlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -230,13 +230,13 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("clig", 1); + spec.Features = addFeature("clig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); - otf = (uiOpenTypeFeatures *) addFeature("hlig", 1); + otf = addFeature("hlig", 1); // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too uiOpenTypeFeaturesAdd(otf, 'h', 'i', 's', 't', 1); next = "\xC3\x9F"; @@ -245,7 +245,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = (uintptr_t) otf; + spec.Value = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -257,7 +257,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("unic", 1); + spec.Features = addFeature("unic", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -268,14 +268,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("pnum", 1); + spec.Features = addFeature("pnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and tabular/monospaced ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("tnum", 1); + spec.Features = addFeature("tnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") numbers"); @@ -287,7 +287,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("sups", 1); + spec.Features = addFeature("sups", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -299,7 +299,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("subs", 1); + spec.Features = addFeature("subs", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -311,7 +311,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ordn", 1); + spec.Features = addFeature("ordn", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -323,7 +323,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("sinf", 1); + spec.Features = addFeature("sinf", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -344,14 +344,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("afrc", 1); + spec.Features = addFeature("afrc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("frac", 1); + spec.Features = addFeature("frac", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -363,14 +363,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("zero", 0); + spec.Features = addFeature("zero", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("zero", 1); + spec.Features = addFeature("zero", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -382,14 +382,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("mgrk", 0); + spec.Features = addFeature("mgrk", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("mgrk", 1); + spec.Features = addFeature("mgrk", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -402,7 +402,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ornm", i); + spec.Features = addFeature("ornm", i); uiAttributedStringSetAttribute(attrstr, &spec, start, end); next = "\xE2\x80\xA2"; } @@ -416,14 +416,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("aalt", 0); + spec.Features = addFeature("aalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("aalt", 1); + spec.Features = addFeature("aalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -435,14 +435,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("titl", 0); + spec.Features = addFeature("titl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("titl", 1); + spec.Features = addFeature("titl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -454,20 +454,20 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("jp78", 1); + spec.Features = addFeature("jp78", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("jp83", 1); + spec.Features = addFeature("jp83", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); - otf = (uiOpenTypeFeatures *) addFeature("onum", 0); + otf = addFeature("onum", 0); // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too // TODO is it always set? uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 0); @@ -477,16 +477,16 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = (uintptr_t) otf; + spec.Features = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - otf = (uiOpenTypeFeatures *) addFeature("onum", 1); + otf = addFeature("onum", 1); uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 1); - spec.Value = (uintptr_t) otf; + spec.Features = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -498,14 +498,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hngl", 0); + spec.Features = addFeature("hngl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hngl", 1); + spec.Features = addFeature("hngl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -517,21 +517,21 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("nalt", 0); + spec.Features = addFeature("nalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("nalt", 1); + spec.Features = addFeature("nalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("nalt", 4); // AAT inverted circle + spec.Features = addFeature("nalt", 4); // AAT inverted circle uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -543,14 +543,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ruby", 0); + spec.Features = addFeature("ruby", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ruby", 1); + spec.Features = addFeature("ruby", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -562,14 +562,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ital", 0); + spec.Features = addFeature("ital", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ital", 1); + spec.Features = addFeature("ital", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -581,14 +581,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("case", 0); + spec.Features = addFeature("case", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("case", 1); + spec.Features = addFeature("case", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -600,14 +600,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cpsp", 0); + spec.Features = addFeature("cpsp", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cpsp", 1); + spec.Features = addFeature("cpsp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -619,28 +619,28 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hkna", 0); + spec.Features = addFeature("hkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hkna", 1); + spec.Features = addFeature("hkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and vertical ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("vkna", 0); + spec.Features = addFeature("vkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("vkna", 1); + spec.Features = addFeature("vkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") kana forms"); @@ -662,7 +662,7 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Value = addFeature(tag, 1); + spec.Features = addFeature(tag, 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); } uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -675,14 +675,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("calt", 0); + spec.Features = addFeature("calt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("calt", 1); + spec.Features = addFeature("calt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -694,14 +694,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("swsh", 0); + spec.Features = addFeature("swsh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("swsh", 1); + spec.Features = addFeature("swsh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -713,14 +713,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cswh", 0); + spec.Features = addFeature("cswh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cswh", 1); + spec.Features = addFeature("cswh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -731,7 +731,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("smcp", 1); + spec.Features = addFeature("smcp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); next = "Petite Caps"; @@ -739,7 +739,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("pcap", 1); + spec.Features = addFeature("pcap", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -749,7 +749,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("c2sp", 1); + spec.Features = addFeature("c2sp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", and "); next = "PETITE UPPERCASES"; @@ -757,7 +757,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("c2pc", 1); + spec.Features = addFeature("c2pc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "."); diff --git a/ui_attrstr.h b/ui_attrstr.h index fc281f99..e6fb6d08 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,9 +19,9 @@ typedef struct uiAttributedString uiAttributedString; // Note: where you say "1 = on", any nonzero value means "on". (TODO) -// TODO ok, we need to figure out what to do about pointer objects: do we copy them or do we keep them safe? especially since we merge attributes... +// TODO just make a separate field for everything? _UI_ENUM(uiAttribute) { - uiAttributeFamily, + uiAttributeFamily, // use Family uiAttributeSize, // use Double uiAttributeWeight, uiAttributeItalic, @@ -34,7 +34,7 @@ _UI_ENUM(uiAttribute) { uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout - uiAttributeFeatures, // object of type uiOpenTypeFeatures + uiAttributeFeatures, // use Features }; _UI_ENUM(uiDrawUnderlineStyle) { @@ -60,7 +60,7 @@ _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); // TODO put above Free? // TODO Copy instead of Clone? -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf); +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); @@ -71,12 +71,14 @@ typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { uiAttribute Type; + const char *Family; uintptr_t Value; double Double; double R; double G; double B; double A; + const uiOpenTypeFeatures *Features; // TODO rename to OpenTypeFeatures? }; // TODO name the foreach return values From 02020e676a9e17c0c025165962a72e945f474802 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 May 2017 00:41:40 -0400 Subject: [PATCH 231/487] Managed attribute spec memory properly. --- common/attrlist.c | 90 +++++++++++++++++++++++++++-------------------- ui_attrstr.h | 5 +-- 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index ec0b12bd..0d354571 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -24,6 +24,47 @@ struct attrlist { struct attr *last; }; +// We need to make local copies of any pointers in uiAttributeSpec. +// If we don't do this, we'll wind up leaking stuff, or worse, prematurely freeing stuff. +// TODO ensure this is docmented +static void attrSetSpec(struct attr *a, uiAttributeSpec *spec) +{ + const char *family; + char *familyCopy; + + a->spec = *spec; + switch (a->spec.Type) { + case uiAttributeFamily: + // TODO UTF-8 validate this? + family = a->spec.Family; + familyCopy = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttributeSpec.Family copy)"); + strcpy(familyCopy, family); + a->spec.Family = familyCopy; + break; + case uiAttributeFeatures: + a->spec.Features = uiOpenTypeFeaturesClone(a->spec.Features); + break; + } +} + +static void attrCopySpec(struct attr *dest, struct attr *src) +{ + attrSetSpec(dest, &(src->spec)); +} + +// Likewise, this is needed to clean up a spec with a pointer when finished. +static void attrClearSpec(struct attr *a) +{ + switch (a->spec.Type) { + case uiAttributeFamily: + uiFree((char *) (a->spec.Family)); + break; + case uiAttributeFeatures: + uiOpenTypeFeaturesFree((uiOpenTypeFeatures *) (a->spec.Features)); + break; + } +} + // if before is NULL, add to the end of the list static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) { @@ -128,6 +169,7 @@ static struct attr *attrDelete(struct attrlist *alist, struct attr *a) struct attr *next; next = attrUnlink(alist, a); + attrClearSpec(a); uiFree(a); return next; } @@ -177,7 +219,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t // we'll need to split the attribute into two b = uiNew(struct attr); - b->spec = a->spec; + attrCopySpec(b, a); b->start = end; b->end = a->end; *tail = b; @@ -221,7 +263,7 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a return NULL; b = uiNew(struct attr); - b->spec = a->spec; + attrCopySpec(b, a); b->start = at; b->end = a->end; @@ -284,39 +326,6 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -static int boolsEqual(struct attr *attr, uiAttributeSpec *spec) -{ - if (attr->spec.Value == 0 && spec->Value == 0) - return 1; - return attr->spec.Value != 0 && spec->Value != 0; -} - -// BCP 47 is ASCII-only -static int asciiStringsEqualCaseFold(const char *a, const char *b) -{ - char c, d; - - for (;;) { - if (*a == *b) { - if (*a == '\0') - return 1; - a++; - b++; - continue; - } - c = *a; - if (c >= 'A' && c <= 'Z') - c += 'a' - 'A'; - d = *b; - if (d >= 'A' && d <= 'Z') - d += 'a' - 'A'; - if (c != d) - return 0; - a++; - b++; - } -} - static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) { if (attr->spec.Type != spec->Type) @@ -325,7 +334,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) case uiAttributeFamily: // TODO should we start copying these strings? // TODO should this be case-insensitive? - return strcmp((char *) (attr->spec.Value), (char *) (spec->Value)) == 0; + return strcmp(attr->spec.Family, spec->Family) == 0; case uiAttributeSize: // TODO use a closest match? return attr->spec.Double == spec->Double; @@ -342,9 +351,11 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; + case uiAttributeFeatures: + // TODO rename it to uiAttributeOpenTypeFeatures? + return uiOpenTypeFeaturesEqual(attr->spec.Features, spec->Features); } - // handles the rest, including pointer comparison for uiAttributeFeatures - // TODO rename it to uiAttributeOpenTypeFeatures? + // handles the rest return attr->spec.Value == spec->Value; } @@ -394,7 +405,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size // if we got here, we know we have to add the attribute before before a = uiNew(struct attr); - a->spec = *spec; + attrSetSpec(a, spec); a->start = start; a->end = end; attrInsertBefore(alist, a, before); @@ -661,6 +672,7 @@ void attrlistFree(struct attrlist *alist) a = alist->first; while (a != NULL) { next = a->next; + attrClearSpec(a); uiFree(a); a = next; } diff --git a/ui_attrstr.h b/ui_attrstr.h index e6fb6d08..d293f590 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -33,7 +33,7 @@ _UI_ENUM(uiAttribute) { // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor - // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout + // TODO note these are copied uiAttributeFeatures, // use Features }; @@ -65,7 +65,7 @@ _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, c _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); _UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); -_UI_EXTERN int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b); +_UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; @@ -82,6 +82,7 @@ struct uiAttributeSpec { }; // TODO name the foreach return values +// TODO make the spec const in a way that doesn't allow fields to be modified? typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor From d979f7a93e2e96e8fd5610df4b216188838ac684 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 May 2017 21:55:10 -0400 Subject: [PATCH 232/487] Updated a stale comment. --- windows/utilwin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 414ae83a..11f9a415 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -5,7 +5,7 @@ // It is not a message-only window, and it is always hidden and disabled. // Its roles: // - It is the initial parent of all controls. When a control loses its parent, it also becomes that control's parent. -// - It handles WM_QUERYENDSESSION and console end session requests. +// - It handles WM_QUERYENDSESSION requests. // - It handles WM_WININICHANGE and forwards the message to any child windows that request it. // - It handles executing functions queued to run by uiQueueMain(). From 1733c28b52517434467824d5f56a0152cce5bd57 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 09:52:38 -0400 Subject: [PATCH 233/487] Added consts to the OpenType implementations. --- darwin/opentype.m | 4 ++-- unix/opentype.c | 5 +++-- windows/opentype.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 25939299..52db9560 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -20,7 +20,7 @@ void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) uiFree(otf); } -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; @@ -82,7 +82,7 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac }]; } -int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { return [a->tags isEqualToDictionary:b->tags]; } diff --git a/unix/opentype.c b/unix/opentype.c index 6ea317c5..6700b83e 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -28,7 +28,7 @@ static void cloneTags(gpointer key, gpointer value, gpointer data) g_hash_table_replace((GHashTable *) data, key, value); } -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; @@ -119,10 +119,11 @@ static GList *copySortedKeys(GHashTable *tags) copy = g_list_copy(k); copy = g_list_sort(copy, tagcmp); // TODO do we free k? the docs contradict themselves + // TODO I already forgot, does g_list_sort() copy, or just change the head? return copy; } -int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { GList *ak, *bk; GList *ai, *bi; diff --git a/windows/opentype.cpp b/windows/opentype.cpp index f3ca36c3..141f8a5f 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -22,7 +22,7 @@ void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) uiFree(otf); } -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; @@ -75,7 +75,7 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac } } -int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { // TODO make sure this is correct return *(a->tags) == *(b->tags); From e356f1c48a8335dac89860d1ffcca0938bc4661c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 13:06:58 -0400 Subject: [PATCH 234/487] Started reworking darwin/attrstr.m to be a lot more sane. --- common/attrlist.c | 1 - darwin/attrstr.m | 112 +++++++++++++++++++++++++++++++++------------- 2 files changed, 80 insertions(+), 33 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 0d354571..ca2937dc 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -332,7 +332,6 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) return 0; switch (attr->spec.Type) { case uiAttributeFamily: - // TODO should we start copying these strings? // TODO should this be case-insensitive? return strcmp(attr->spec.Family, spec->Family) == 0; case uiAttributeSize: diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 556b09ad..4ce242d1 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -60,48 +60,95 @@ void uninitUnderlineColors(void) // TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) +// TODO restructure all this so the important details at the top are below with the combined font attributes type? struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableDictionary *converted; + NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are combinedFontAttr objects uiDrawFontDescriptor *defaultFont; NSMutableArray *backgroundBlocks; }; -#define maxFeatures 32 +@interface combinedFontAttr : NSObject +@property const char *family; +@property double size; +@property uiDrawTextWeight weight; +@property uiDrawTextItalic italic; +@property uiDrawTextStretch stretch; +@property const uiOpenTypeFeatures *features; +- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; +- (BOOL)same:(combinedFontAttr *)b; +@end -struct fontParams { - uiDrawFontDescriptor desc; - uint16_t featureTypes[maxFeatures]; - uint16_t featureSelectors[maxFeatures]; - size_t nFeatures; -}; +@implementation combinedFontAttr + +- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont +{ + self = [super init]; + if (self) { + self.family = defaultFont->Family; + self.size = defaultFont->Size; + self.weight = defaultFont->Weight; + self.italic = defaultFont->Italic; + self.stretch = defaultFont->Stretch; + self.features = NULL; + } + return self; +} + +// TODO deduplicate this with common/attrlist.c +- (BOOL)same:(combinedFontAttr *)b +{ + // TODO should this be case-insensitive? + if (strcmp(self.family, b.family) != 0) + return NO; + // TODO use a closest match? + if (self.size != b.size) + return NO; + if (self.weight != b.weight) + return NO; + if (self.italic != b.italic) + return NO; + if (self.stretch != b.stretch) + return NO; + // TODO make this part of uiOpenTypeFeaturesEqual() on all platforms + if (self.features == NULL && b.features == NULL) + return YES; + if (self.features != NULL && b.features == NULL) + return NO; + if (self.features == NULL && b.features != NULL) + return NO; + if (!uiOpenTypeFeaturesEqual(self.features, b.features)) + return NO; + return YES; +} + +@end static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; NSNumber *n; - struct fontParams *new; + combinedFontAttr *new; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - if ([p->converted objectForKey:n] != nil) + if ([p->combinedFontAttrs objectForKey:n] != nil) continue; - new = uiNew(struct fontParams); - new->desc = *(p->defaultFont); - [p->converted setObject:[NSValue valueWithPointer:new] forKey:n]; + new = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + [p->combinedFontAttrs setObject:new forKey:n]; } } -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(struct fontParams *fp)) +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(combinedFontAttr *cfa)) { size_t i; NSNumber *n; - NSValue *v; + combinedFontAttr *cfa; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - v = (NSValue *) [p->converted objectForKey:n]; - adj((struct fontParams *) [v pointerValue]); + v = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + adj(cfa); } } @@ -146,6 +193,7 @@ struct aatParam { static void doAAT(uint16_t type, uint16_t selector, void *data) { +#if 0 /* TODO */ struct aatParam *p = (struct aatParam *) data; ensureFontInRange(p->p, p->start, p->end); @@ -158,6 +206,7 @@ static void doAAT(uint16_t type, uint16_t selector, void *data) // TODO move this check to the top like in the drawtext example? and all the other instances of this? } }); +#endif } static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) @@ -169,7 +218,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t backgroundBlock block; int32_t us; CFNumberRef num; - struct aatParam ap; ostart = start; oend = end; @@ -180,32 +228,32 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Family = (char *) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.family = spec->Family; }); break; case uiAttributeSize: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Size = spec->Double; + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Weight = (uiDrawTextWeight) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Italic = (uiDrawTextItalic) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.stretch = (uiDrawTextStretch) (spec->Value); }); break; case uiAttributeColor: @@ -259,10 +307,10 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t CFRelease(color); break; case uiAttributeFeatures: - ap.p = p; - ap.start = start; - ap.end = end; - openTypeToAAT((uiOpenTypeFeatures *) (spec->Value), doAAT, &ap); + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.features = spec->Features; + }); break; default: // TODO complain From c4dd85bece6a0a39b5e0cc0bc5986fdea9694377 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 13:18:13 -0400 Subject: [PATCH 235/487] More reworking the OS X attributed string code. Now we need to rework the AAT code somewhat too. --- darwin/attrstr.m | 34 ++++++++++++++++++++++------------ darwin/fontmatch.m | 4 +--- darwin/uipriv_darwin.h | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 4ce242d1..60120af9 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -61,6 +61,7 @@ void uninitUnderlineColors(void) // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? +// TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are combinedFontAttr objects @@ -77,6 +78,7 @@ struct foreachParams { @property const uiOpenTypeFeatures *features; - (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; - (BOOL)same:(combinedFontAttr *)b; +- (CTFontRef)toCTFont; @end @implementation combinedFontAttr @@ -85,6 +87,7 @@ struct foreachParams { { self = [super init]; if (self) { + // TODO define behaviors if defaultFont->Family or any attribute Family is NULL, same with other invalid values self.family = defaultFont->Family; self.size = defaultFont->Size; self.weight = defaultFont->Weight; @@ -122,6 +125,25 @@ struct foreachParams { return YES; } +- (CTFontRef)toCTFont +{ + uiDrawFontDescriptor uidesc; + CTFontDescriptorRef desc; + CTFontRef font; + + uidesc.Family = self.family; + uidesc.Size = self.size; + uidesc.Weight = self.weight; + uidesc.Italic = self.italic; + uidesc.Stretch = self.stretch; + desc = fontdescToCTFontDescriptor(&uidesc); + if (self.features != NULL) + desc = fontdescAppendFeatures(desc, self.features); + font = CTFontCreateWithFontDescriptor(desc, self.size, NULL); + CFRelease(desc); // TODO correct? + return font; +} + @end static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -319,18 +341,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } -static CTFontRef fontdescToCTFont(struct fontParams *fp) -{ - CTFontDescriptorRef desc; - CTFontRef font; - - desc = fontdescToCTFontDescriptor(&(fp->desc)); - desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures); - font = CTFontCreateWithFontDescriptor(desc, fp->desc.Size, NULL); - CFRelease(desc); // TODO correct? - return font; -} - static void applyAndFreeFontAttributes(struct foreachParams *p) { [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 0c87a427..ba740933 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -255,9 +255,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) } // fortunately features that aren't supported are simply ignored, so we can copy them all in -// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore -// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! -CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n) +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; CFMutableArrayRef outerArray; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c22c82b6..4020dd27 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -143,7 +143,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m From 91bfceaf71125c87f4d2330c3074ef2166ad2277 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 14:00:58 -0400 Subject: [PATCH 236/487] And FINALLY cleaned up all the AAT nonsense. Much saner now. --- darwin/aat.m | 162 +++++++++++++++++++---------------------- darwin/fontmatch.m | 45 ++---------- darwin/opentype.m | 51 ++++++++++++- darwin/uipriv_darwin.h | 4 +- ui_attrstr.h | 3 +- unix/opentype.c | 2 +- windows/opentype.cpp | 2 +- 7 files changed, 137 insertions(+), 132 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index 0c964e05..d46a3506 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,25 +1,18 @@ // 14 february 2017 #import "uipriv_darwin.h" -struct openTypeAATParams { - void (*doAAT)(uint16_t type, uint16_t selector, void *data); - void *data; -}; - -#define pcall(p, type, selector) ((*(p->doAAT))(type, selector, p->data)) - -static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, struct openTypeAATParams *p) +static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, aatBlock f) { // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) if (value != 0) { - pcall(p, type, ifTrue); + f(type, ifTrue); return; } - pcall(p, type, ifFalse); + f(type, ifFalse); } // TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) -static int foreach(char a, char b, char c, char d, uint32_t value, void *data) +void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) { struct openTypeAATParams *p = (struct openTypeAATParams *) data; @@ -28,25 +21,25 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) boolspec(value, kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector, - p); + f); break; case mkTag('r', 'l', 'i', 'g'): boolspec(value, kLigaturesType, kRequiredLigaturesOnSelector, kRequiredLigaturesOffSelector, - p); + f); break; case mkTag('d', 'l', 'i', 'g'): boolspec(value, kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector, - p); + f); break; case mkTag('c', 'l', 'i', 'g'): boolspec(value, kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector, - p); + f); break; case mkTag('h', 'l', 'i', 'g'): // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too @@ -54,117 +47,117 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) boolspec(value, kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector, - p); + f); break; case mkTag('u', 'n', 'i', 'c'): // TODO is this correct, or should we provide an else case? if (value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table - pcall(p, kLetterCaseType, 14); + f(p, kLetterCaseType, 14); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('p', 'n', 'u', 'm'): if (value != 0) - pcall(p, kNumberSpacingType, kProportionalNumbersSelector); + f(p, kNumberSpacingType, kProportionalNumbersSelector); break; case mkTag('t', 'n', 'u', 'm'): if (value != 0) - pcall(p, kNumberSpacingType, kMonospacedNumbersSelector); + f(p, kNumberSpacingType, kMonospacedNumbersSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('s', 'u', 'p', 's'): if (value != 0) - pcall(p, kVerticalPositionType, kSuperiorsSelector); + f(p, kVerticalPositionType, kSuperiorsSelector); break; case mkTag('s', 'u', 'b', 's'): if (value != 0) - pcall(p, kVerticalPositionType, kInferiorsSelector); + f(p, kVerticalPositionType, kInferiorsSelector); break; case mkTag('o', 'r', 'd', 'n'): if (value != 0) - pcall(p, kVerticalPositionType, kOrdinalsSelector); + f(p, kVerticalPositionType, kOrdinalsSelector); break; case mkTag('s', 'i', 'n', 'f'): if (value != 0) - pcall(p, kVerticalPositionType, kScientificInferiorsSelector); + f(p, kVerticalPositionType, kScientificInferiorsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('a', 'f', 'r', 'c'): if (value != 0) - pcall(p, kFractionsType, kVerticalFractionsSelector); + f(p, kFractionsType, kVerticalFractionsSelector); break; case mkTag('f', 'r', 'a', 'c'): if (value != 0) - pcall(p, kFractionsType, kDiagonalFractionsSelector); + f(p, kFractionsType, kDiagonalFractionsSelector); break; case mkTag('z', 'e', 'r', 'o'): boolspec(value, kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector, - p); + f); break; case mkTag('m', 'g', 'r', 'k'): boolspec(value, kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector, - p); + f); break; case mkTag('o', 'r', 'n', 'm'): - pcall(p, kOrnamentSetsType, (uint16_t) value); + f(p, kOrnamentSetsType, (uint16_t) value); break; case mkTag('a', 'a', 'l', 't'): - pcall(p, kCharacterAlternativesType, (uint16_t) value); + f(p, kCharacterAlternativesType, (uint16_t) value); break; case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - pcall(p, kStyleOptionsType, kTitlingCapsSelector); + f(p, kStyleOptionsType, kTitlingCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('t', 'r', 'a', 'd'): if (value != 0) - pcall(p, kCharacterShapeType, kTraditionalCharactersSelector); + f(p, kCharacterShapeType, kTraditionalCharactersSelector); break; case mkTag('s', 'm', 'p', 'l'): if (value != 0) - pcall(p, kCharacterShapeType, kSimplifiedCharactersSelector); + f(p, kCharacterShapeType, kSimplifiedCharactersSelector); break; case mkTag('j', 'p', '7', '8'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS1978CharactersSelector); + f(p, kCharacterShapeType, kJIS1978CharactersSelector); break; case mkTag('j', 'p', '8', '3'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS1983CharactersSelector); + f(p, kCharacterShapeType, kJIS1983CharactersSelector); break; case mkTag('j', 'p', '9', '0'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS1990CharactersSelector); + f(p, kCharacterShapeType, kJIS1990CharactersSelector); break; case mkTag('e', 'x', 'p', 't'): if (value != 0) - pcall(p, kCharacterShapeType, kExpertCharactersSelector); + f(p, kCharacterShapeType, kExpertCharactersSelector); break; case mkTag('j', 'p', '0', '4'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS2004CharactersSelector); + f(p, kCharacterShapeType, kJIS2004CharactersSelector); break; case mkTag('h', 'o', 'j', 'o'): if (value != 0) - pcall(p, kCharacterShapeType, kHojoCharactersSelector); + f(p, kCharacterShapeType, kHojoCharactersSelector); break; case mkTag('n', 'l', 'c', 'k'): if (value != 0) - pcall(p, kCharacterShapeType, kNLCCharactersSelector); + f(p, kCharacterShapeType, kNLCCharactersSelector); break; case mkTag('t', 'n', 'a', 'm'): if (value != 0) - pcall(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); + f(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); break; case mkTag('o', 'n', 'u', 'm'): @@ -173,201 +166,201 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) case mkTag('l', 'n', 'u', 'm'): // TODO is this correct, or should we provide an else case? if (value != 0) - pcall(p, kNumberCaseType, kLowerCaseNumbersSelector); + f(p, kNumberCaseType, kLowerCaseNumbersSelector); break; case mkTag('h', 'n', 'g', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - pcall(p, kTransliterationType, kHanjaToHangulSelector); + f(p, kTransliterationType, kHanjaToHangulSelector); break; case mkTag('n', 'a', 'l', 't'): - pcall(p, kAnnotationType, (uint16_t) value); + f(p, kAnnotationType, (uint16_t) value); break; case mkTag('r', 'u', 'b', 'y'): // include this for completeness boolspec(value, kRubyKanaType, kRubyKanaSelector, kNoRubyKanaSelector, - p); + f); // this is the current one boolspec(value, kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector, - p); + f); break; case mkTag('i', 't', 'a', 'l'): // include this for completeness boolspec(value, kItalicCJKRomanType, kCJKItalicRomanSelector, kNoCJKItalicRomanSelector, - p); + f); // this is the current one boolspec(value, kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector, - p); + f); break; case mkTag('c', 'a', 's', 'e'): boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector, - p); + f); break; case mkTag('c', 'p', 's', 'p'): boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector, - p); + f); break; case mkTag('h', 'k', 'n', 'a'): boolspec(value, kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, - p); + f); break; case mkTag('v', 'k', 'n', 'a'): boolspec(value, kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector, - p); + f); break; case mkTag('s', 's', '0', '1'): boolspec(value, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, - p); + f); break; case mkTag('s', 's', '0', '2'): boolspec(value, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, - p); + f); break; case mkTag('s', 's', '0', '3'): boolspec(value, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, - p); + f); break; case mkTag('s', 's', '0', '4'): boolspec(value, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, - p); + f); break; case mkTag('s', 's', '0', '5'): boolspec(value, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, - p); + f); break; case mkTag('s', 's', '0', '6'): boolspec(value, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, - p); + f); break; case mkTag('s', 's', '0', '7'): boolspec(value, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, - p); + f); break; case mkTag('s', 's', '0', '8'): boolspec(value, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, - p); + f); break; case mkTag('s', 's', '0', '9'): boolspec(value, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, - p); + f); break; case mkTag('s', 's', '1', '0'): boolspec(value, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '1'): boolspec(value, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '2'): boolspec(value, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, - p); + f); break; case mkTag('s', 's', '1', '3'): boolspec(value, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '4'): boolspec(value, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '5'): boolspec(value, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '6'): boolspec(value, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '7'): boolspec(value, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '8'): boolspec(value, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '9'): boolspec(value, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, - p); + f); break; case mkTag('s', 's', '2', '0'): boolspec(value, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, - p); + f); break; case mkTag('c', 'a', 'l', 't'): boolspec(value, kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector, - p); + f); break; case mkTag('s', 'w', 's', 'h'): boolspec(value, kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector, - p); + f); break; case mkTag('c', 's', 'w', 'h'): boolspec(value, kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector, - p); + f); break; // TODO will the following handle all cases properly, or are elses going to be needed? @@ -375,35 +368,26 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) if (value != 0) { // include this for compatibility (some fonts that come with OS X still use this!) // TODO make it boolean? - pcall(p, kLetterCaseType, kSmallCapsSelector); + f(p, kLetterCaseType, kSmallCapsSelector); // this is the current one - pcall(p, kLowerCaseType, kLowerCaseSmallCapsSelector); + f(p, kLowerCaseType, kLowerCaseSmallCapsSelector); } break; case mkTag('p', 'c', 'a', 'p'): if (value != 0) - pcall(p, kLowerCaseType, kLowerCasePetiteCapsSelector); + f(p, kLowerCaseType, kLowerCasePetiteCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('c', '2', 's', 'c'): if (value != 0) - pcall(p, kUpperCaseType, kUpperCaseSmallCapsSelector); + f(p, kUpperCaseType, kUpperCaseSmallCapsSelector); break; case mkTag('c', '2', 'p', 'c'): if (value != 0) - pcall(p, kUpperCaseType, kUpperCasePetiteCapsSelector); + f(p, kUpperCaseType, kUpperCasePetiteCapsSelector); break; } // TODO handle this properly - return 0; -} - -void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data) -{ - struct openTypeAATParams p; - - p.doAAT = doAAT; - p.data = data; - uiOpenTypeFeaturesForEach(otf, foreach, &p); + // (it used to return 0 when this still returned the number of selectors produced but IDK what properly is anymore) } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index ba740933..56b8991d 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -258,50 +258,21 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; - CFMutableArrayRef outerArray; - CFDictionaryRef innerDict; - CFNumberRef numType, numSelector; - const void *keys[2], *values[2]; - size_t i; - - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) { - // TODO - } - keys[0] = kCTFontFeatureTypeIdentifierKey; - keys[1] = kCTFontFeatureSelectorIdentifierKey; - for (i = 0; i < n; i++) { - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (types + i)); - numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (selectors + i)); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); - } + CFMutableArrayRef featuresArray; + CFDictionaryRef attrs; + featuresArray = otfToFeaturesArray(otf); keys[0] = kCTFontFeatureSettingsAttribute; - values[0] = outerArray; - innerDict = CFDictionaryCreate(NULL, + values[0] = featuresArray; + attrs = CFDictionaryCreate(NULL, keys, values, 1, // TODO are these correct? &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease(outerArray); - new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict); + CFRelease(featuresArray); + new = CTFontDescriptorCreateCopyWithAttributes(desc, attrs); + CFRelease(attrs); CFRelease(desc); - CFRelease(innerDict); return new; } diff --git a/darwin/opentype.m b/darwin/opentype.m index 52db9560..d6bd6652 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -63,7 +63,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char return 1; } -void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { [otf->tags enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSNumber *tn = (NSNumber *) key; @@ -87,4 +87,51 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature return [a->tags isEqualToDictionary:b->tags]; } -// actual conversion to a feature dictionary is handled in aat.m; see there for details +// TODO explain all this +// TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) +// TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? +static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void *data) +{ + CFMutableArrayRef outerArray = (CFMutableArrayRef) data; + const void *keys[2], *values[2]; + + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; + openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { + CFDictionaryRef innerDict; + CFNumberRef numType, numSelector; + + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&type)); + numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&selector)); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(p->outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + }); + // TODO + return 0; +} + +CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) +{ + CFMutableArrayRef outerArray; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) { + // TODO + } + uiOpenTypeFeaturesForEach(otf, otfArrayForEach, outerArray); + return outerArray; +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 4020dd27..56a576d4 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -153,7 +153,8 @@ typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, doub extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); // aat.m -extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data); +typedef void (^aatBlock)(uint16_t type, uint16_t selector); +extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); // opentype.m // TODO this is only used by opentype.m and aat.m; figure out some better way to handle this @@ -164,6 +165,7 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, (x8tox32(b) << 16) | \ (x8tox32(c) << 8) | \ x8tox32(d)) +extern CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf); // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; diff --git a/ui_attrstr.h b/ui_attrstr.h index d293f590..ad4169e9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -64,7 +64,8 @@ _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); -_UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); +// TODO make other enumerators const (and in general const-correct everything) +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; diff --git a/unix/opentype.c b/unix/opentype.c index 6700b83e..f9d14e09 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -96,7 +96,7 @@ static void foreach(gpointer key, gpointer value, gpointer data) (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } -void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { struct otfForEach ofe; diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 141f8a5f..b27b7d7a 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -57,7 +57,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char return 1; } -void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { tagmap::const_iterator iter, end; From 06becce34ce834af874ec08beea6e1cdd838bb8d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 14:38:25 -0400 Subject: [PATCH 237/487] And finished the font attribute rewrite on OS X. Now to test. --- darwin/aat.m | 2 + darwin/attrstr.m | 96 +++++++++++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 37 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index d46a3506..c11abccd 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,6 +1,8 @@ // 14 february 2017 #import "uipriv_darwin.h" +// TODO explain the purpose of this file + static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, aatBlock f) { // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 60120af9..ba69b9ed 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -146,6 +146,7 @@ struct foreachParams { @end +// TODO merge this with adjustFontInRange() (and TODO figure out why they were separate in the first place; probably Windows?) static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; @@ -207,30 +208,6 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -struct aatParam { - struct foreachParams *p; - size_t start; - size_t end; -}; - -static void doAAT(uint16_t type, uint16_t selector, void *data) -{ -#if 0 /* TODO */ - struct aatParam *p = (struct aatParam *) data; - - ensureFontInRange(p->p, p->start, p->end); - adjustFontInRange(p->p, p->start, p->end, ^(struct fontParams *fp) { - fp->featureTypes[fp->nFeatures] = type; - fp->featureSelectors[fp->nFeatures] = selector; - fp->nFeatures++; - if (fp->nFeatures == maxFeatures) { - // TODO - // TODO move this check to the top like in the drawtext example? and all the other instances of this? - } - }); -#endif -} - static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -341,22 +318,67 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } +static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) +{ + if (a == nil && b == nil) + return YES; + if (a == nil || b == nil) + return NO; + return [a same:b]; +} + static void applyAndFreeFontAttributes(struct foreachParams *p) { - [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { - struct fontParams *fp; - CTFontRef font; - CFRange range; + CFIndex i; + combinedFontAttr *cfa, *cfab; + CTFontRef defaultFont; + CTFontRef font; + CFRange range; - fp = (struct fontParams *) [val pointerValue]; - font = fontdescToCTFont(fp); - range.location = [key integerValue]; - // TODO this is wrong for surrogate pairs - range.length = 1; + // first get the default font as a CTFontRef + cfa = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + defaultFont = [cfa toCTFont]; + [cfa release]; + + // now go through, fililng in the font attribute for successive ranges of identical combinedFontAttrs + // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself + // this also avoids breaking apart surrogate pairs (though IIRC OS X doing the something similar itself might make this a non-issue here) + cfa = nil; + n = CFAttributedStringGetLength(p->mas); + range.location = 0; + for (i = 0; i < n; i++) { + NSNumber *nn; + + nn = [NSNumber numberWithInteger:i]; + cfab = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; + if (cfaIsEqual(cfa, cfab)) + continue; + + // the font has changed; write out the old one + range.length = i - range.location; + if (cfa == nil) { + font = defaultFont; + CFRetain(font); + } else + font = [cfa toCTFont]; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); - uiFree(fp); - }]; + // and start this run + cfa = cfab; + range.location = i; + } + + // and finally, write out the last range + range.length = i - range.location; + if (cfa == nil) { + font = defaultFont; + CFRetain(font); + } else + font = [cfa toCTFont]; + CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); + CFRelease(font); + + CFRelease(defaultFont); } static const CTTextAlignment ctaligns[] = { @@ -424,12 +446,12 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray CFAttributedStringBeginEditing(mas); fep.mas = mas; - fep.converted = [NSMutableDictionary new]; + fep.combinedFontAttrs = [NSMutableDictionary new]; fep.defaultFont = p->DefaultFont; fep.backgroundBlocks = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFontAttributes(&fep); - [fep.converted release]; + [fep.combinedFontAttrs release]; CFAttributedStringEndEditing(mas); *backgroundBlocks = fep.backgroundBlocks; From 01df4631f68f1edf695080d1b58249a50eeb1647 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 15:24:31 -0400 Subject: [PATCH 238/487] And finished integrating the new attributed string stuff on OS X. --- common/attrlist.c | 2 +- darwin/aat.m | 62 ++++++++++++++++------------------ darwin/attrstr.m | 14 +++++--- darwin/fontmatch.m | 3 +- darwin/opentype.m | 9 ++--- examples/drawtext/attributes.c | 4 +-- ui_attrstr.h | 1 + 7 files changed, 50 insertions(+), 45 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index ca2937dc..e50ec141 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -60,7 +60,7 @@ static void attrClearSpec(struct attr *a) uiFree((char *) (a->spec.Family)); break; case uiAttributeFeatures: - uiOpenTypeFeaturesFree((uiOpenTypeFeatures *) (a->spec.Features)); + uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) (a->spec.Features)); break; } } diff --git a/darwin/aat.m b/darwin/aat.m index c11abccd..0c3fd5fa 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -16,8 +16,6 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if // TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) { - struct openTypeAATParams *p = (struct openTypeAATParams *) data; - switch (mkTag(a, b, c, d)) { case mkTag('l', 'i', 'g', 'a'): boolspec(value, kLigaturesType, @@ -55,45 +53,45 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) // TODO is this correct, or should we provide an else case? if (value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table - f(p, kLetterCaseType, 14); + f(kLetterCaseType, 14); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('p', 'n', 'u', 'm'): if (value != 0) - f(p, kNumberSpacingType, kProportionalNumbersSelector); + f(kNumberSpacingType, kProportionalNumbersSelector); break; case mkTag('t', 'n', 'u', 'm'): if (value != 0) - f(p, kNumberSpacingType, kMonospacedNumbersSelector); + f(kNumberSpacingType, kMonospacedNumbersSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('s', 'u', 'p', 's'): if (value != 0) - f(p, kVerticalPositionType, kSuperiorsSelector); + f(kVerticalPositionType, kSuperiorsSelector); break; case mkTag('s', 'u', 'b', 's'): if (value != 0) - f(p, kVerticalPositionType, kInferiorsSelector); + f(kVerticalPositionType, kInferiorsSelector); break; case mkTag('o', 'r', 'd', 'n'): if (value != 0) - f(p, kVerticalPositionType, kOrdinalsSelector); + f(kVerticalPositionType, kOrdinalsSelector); break; case mkTag('s', 'i', 'n', 'f'): if (value != 0) - f(p, kVerticalPositionType, kScientificInferiorsSelector); + f(kVerticalPositionType, kScientificInferiorsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('a', 'f', 'r', 'c'): if (value != 0) - f(p, kFractionsType, kVerticalFractionsSelector); + f(kFractionsType, kVerticalFractionsSelector); break; case mkTag('f', 'r', 'a', 'c'): if (value != 0) - f(p, kFractionsType, kDiagonalFractionsSelector); + f(kFractionsType, kDiagonalFractionsSelector); break; case mkTag('z', 'e', 'r', 'o'): @@ -109,57 +107,57 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) f); break; case mkTag('o', 'r', 'n', 'm'): - f(p, kOrnamentSetsType, (uint16_t) value); + f(kOrnamentSetsType, (uint16_t) value); break; case mkTag('a', 'a', 'l', 't'): - f(p, kCharacterAlternativesType, (uint16_t) value); + f(kCharacterAlternativesType, (uint16_t) value); break; case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - f(p, kStyleOptionsType, kTitlingCapsSelector); + f(kStyleOptionsType, kTitlingCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('t', 'r', 'a', 'd'): if (value != 0) - f(p, kCharacterShapeType, kTraditionalCharactersSelector); + f(kCharacterShapeType, kTraditionalCharactersSelector); break; case mkTag('s', 'm', 'p', 'l'): if (value != 0) - f(p, kCharacterShapeType, kSimplifiedCharactersSelector); + f(kCharacterShapeType, kSimplifiedCharactersSelector); break; case mkTag('j', 'p', '7', '8'): if (value != 0) - f(p, kCharacterShapeType, kJIS1978CharactersSelector); + f(kCharacterShapeType, kJIS1978CharactersSelector); break; case mkTag('j', 'p', '8', '3'): if (value != 0) - f(p, kCharacterShapeType, kJIS1983CharactersSelector); + f(kCharacterShapeType, kJIS1983CharactersSelector); break; case mkTag('j', 'p', '9', '0'): if (value != 0) - f(p, kCharacterShapeType, kJIS1990CharactersSelector); + f(kCharacterShapeType, kJIS1990CharactersSelector); break; case mkTag('e', 'x', 'p', 't'): if (value != 0) - f(p, kCharacterShapeType, kExpertCharactersSelector); + f(kCharacterShapeType, kExpertCharactersSelector); break; case mkTag('j', 'p', '0', '4'): if (value != 0) - f(p, kCharacterShapeType, kJIS2004CharactersSelector); + f(kCharacterShapeType, kJIS2004CharactersSelector); break; case mkTag('h', 'o', 'j', 'o'): if (value != 0) - f(p, kCharacterShapeType, kHojoCharactersSelector); + f(kCharacterShapeType, kHojoCharactersSelector); break; case mkTag('n', 'l', 'c', 'k'): if (value != 0) - f(p, kCharacterShapeType, kNLCCharactersSelector); + f(kCharacterShapeType, kNLCCharactersSelector); break; case mkTag('t', 'n', 'a', 'm'): if (value != 0) - f(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); + f(kCharacterShapeType, kTraditionalNamesCharactersSelector); break; case mkTag('o', 'n', 'u', 'm'): @@ -168,15 +166,15 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) case mkTag('l', 'n', 'u', 'm'): // TODO is this correct, or should we provide an else case? if (value != 0) - f(p, kNumberCaseType, kLowerCaseNumbersSelector); + f(kNumberCaseType, kLowerCaseNumbersSelector); break; case mkTag('h', 'n', 'g', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - f(p, kTransliterationType, kHanjaToHangulSelector); + f(kTransliterationType, kHanjaToHangulSelector); break; case mkTag('n', 'a', 'l', 't'): - f(p, kAnnotationType, (uint16_t) value); + f(kAnnotationType, (uint16_t) value); break; case mkTag('r', 'u', 'b', 'y'): // include this for completeness @@ -370,24 +368,24 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) if (value != 0) { // include this for compatibility (some fonts that come with OS X still use this!) // TODO make it boolean? - f(p, kLetterCaseType, kSmallCapsSelector); + f(kLetterCaseType, kSmallCapsSelector); // this is the current one - f(p, kLowerCaseType, kLowerCaseSmallCapsSelector); + f(kLowerCaseType, kLowerCaseSmallCapsSelector); } break; case mkTag('p', 'c', 'a', 'p'): if (value != 0) - f(p, kLowerCaseType, kLowerCasePetiteCapsSelector); + f(kLowerCaseType, kLowerCasePetiteCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('c', '2', 's', 'c'): if (value != 0) - f(p, kUpperCaseType, kUpperCaseSmallCapsSelector); + f(kUpperCaseType, kUpperCaseSmallCapsSelector); break; case mkTag('c', '2', 'p', 'c'): if (value != 0) - f(p, kUpperCaseType, kUpperCasePetiteCapsSelector); + f(kUpperCaseType, kUpperCasePetiteCapsSelector); break; } // TODO handle this properly diff --git a/darwin/attrstr.m b/darwin/attrstr.m index ba69b9ed..6cf412a4 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -131,7 +131,8 @@ struct foreachParams { CTFontDescriptorRef desc; CTFontRef font; - uidesc.Family = self.family; + // TODO const-correct uiDrawFontDescriptor or change this function below + uidesc.Family = (char *) (self.family); uidesc.Size = self.size; uidesc.Weight = self.weight; uidesc.Italic = self.italic; @@ -170,7 +171,7 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - v = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + cfa = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; adj(cfa); } } @@ -329,7 +330,7 @@ static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) static void applyAndFreeFontAttributes(struct foreachParams *p) { - CFIndex i; + CFIndex i, n; combinedFontAttr *cfa, *cfab; CTFontRef defaultFont; CTFontRef font; @@ -349,6 +350,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) for (i = 0; i < n; i++) { NSNumber *nn; + // TODO use NSValue or some other method of NSNumber nn = [NSNumber numberWithInteger:i]; cfab = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; if (cfaIsEqual(cfa, cfab)) @@ -357,6 +359,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) // the font has changed; write out the old one range.length = i - range.location; if (cfa == nil) { + // TODO this isn't necessary because of default font shenanigans below font = defaultFont; CFRetain(font); } else @@ -371,6 +374,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) // and finally, write out the last range range.length = i - range.location; if (cfa == nil) { + // TODO likewise font = defaultFont; CFRetain(font); } else @@ -414,7 +418,6 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray CFAttributedStringRef base; CFMutableAttributedStringRef mas; struct foreachParams fep; - struct fontParams ffp; cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); if (cfstr == NULL) { @@ -426,11 +429,12 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray if (defaultAttrs == NULL) { // TODO } - memset(&ffp, 0, sizeof (struct fontParams)); +#if 0 /* TODO */ ffp.desc = *(p->DefaultFont); defaultCTFont = fontdescToCTFont(&ffp); CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); CFRelease(defaultCTFont); +#endif ps = mkParagraphStyle(p); CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); CFRelease(ps); diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 56b8991d..abd38c32 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -258,8 +258,9 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; - CFMutableArrayRef featuresArray; + CFArrayRef featuresArray; CFDictionaryRef attrs; + const void *keys[1], *values[1]; featuresArray = otfToFeaturesArray(otf); keys[0] = kCTFontFeatureSettingsAttribute; diff --git a/darwin/opentype.m b/darwin/opentype.m index d6bd6652..00a76200 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -93,14 +93,15 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; - const void *keys[2], *values[2]; - keys[0] = kCTFontFeatureTypeIdentifierKey; - keys[1] = kCTFontFeatureSelectorIdentifierKey; openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { CFDictionaryRef innerDict; CFNumberRef numType, numSelector; + // not well documented, but fixed-size arrays don't support __block either (VLAs are documented as being unsupported) + const void *keys[2], *values[2]; + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; numType = CFNumberCreate(NULL, kCFNumberSInt16Type, (const SInt16 *) (&type)); numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, @@ -115,7 +116,7 @@ static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void if (innerDict == NULL) { // TODO } - CFArrayAppendValue(p->outerArray, innerDict); + CFArrayAppendValue(outerArray, innerDict); CFRelease(innerDict); CFRelease(numSelector); CFRelease(numType); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 69bbe032..f15ba070 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -170,7 +170,7 @@ static void setupAttributedString(void) spec.A = 0.75; uiAttributedStringSetAttribute(attrstr, &spec, start + 12, end); spec.Type = uiAttributeFamily; - spec.Value = "Helvetica"; + spec.Family = "Helvetica"; uiAttributedStringSetAttribute(attrstr, &spec, start + 8, end - 1); spec.Type = uiAttributeBackground; spec.R = 1.0; @@ -245,7 +245,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = otf; + spec.Features = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); diff --git a/ui_attrstr.h b/ui_attrstr.h index ad4169e9..0d1b9d81 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -256,6 +256,7 @@ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayo typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once? _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); From 81a82723d0cbee854c113043d2f57e9584ab9afd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 16:57:25 -0400 Subject: [PATCH 239/487] Fixed the Windows code to run after the recent changes. Now to decide whether to clean it up like we did the OS X code... --- windows/attrstr.cpp | 6 +++--- windows/opentype.cpp | 2 +- windows/uipriv_windows.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 8f5e0adf..be23961e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -40,7 +40,7 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e } } -static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) +static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, const uiOpenTypeFeatures *otf) { IDWriteTypography *dt; size_t i; @@ -89,7 +89,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t range.length = end - start; switch (spec->Type) { case uiAttributeFamily: - wfamily = toUTF16((char *) (spec->Value)); + wfamily = toUTF16(spec->Family); hr = p->layout->SetFontFamilyName(wfamily, range); if (hr != S_OK) logHRESULT(L"error applying family name attribute", hr); @@ -196,7 +196,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); + setFeaturesInRange(p, start, end, spec->Features); break; default: // TODO complain diff --git a/windows/opentype.cpp b/windows/opentype.cpp index b27b7d7a..0275d916 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -81,7 +81,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature return *(a->tags) == *(b->tags); } -IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf) +IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf) { IDWriteTypography *dt; tagmap::const_iterator iter, end; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index aa39dd07..f5adacdd 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -164,4 +164,4 @@ extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); // opentype.cpp -extern IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf); +extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); From c61bbfe5c88b105ccea771c2856a5e3bc511962f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 18:46:30 -0400 Subject: [PATCH 240/487] Updated the GTK+ code to the newest changes. I *do* need to fix this one. --- unix/attrstr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 7bf556c9..21fe108c 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -120,7 +120,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t switch (spec->Type) { case uiAttributeFamily: addattr(p, start, end, - pango_attr_family_new((const char *) (spec->Value))); + pango_attr_family_new(spec->Family)); break; case uiAttributeSize: addattr(p, start, end, @@ -199,8 +199,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - // TODO ensure the parentheses around spec->Value are provided on Windows - setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); + // TODO ensure the parentheses around spec->Value are provided on Windows (TODO still relevant?) + setFeaturesInRange(p, start, end, spec->Features); break; default: // TODO complain From 2e982190094a3a2416c218d1482a49fa160ee9a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 19:07:01 -0400 Subject: [PATCH 241/487] And fixed the whole OpenType features nonsense on GTK+, since we now group all OpenType features together. We're now much closer to pushing this back into master! --- unix/attrstr.c | 81 +++++++--------------------------------------- unix/opentype.c | 4 +-- unix/uipriv_unix.h | 2 +- 3 files changed, 14 insertions(+), 73 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 21fe108c..ad620aed 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -3,57 +3,25 @@ // TODO pango alpha attributes turn 0 into 65535 :| -// we need to collect all the OpenType features and background blocks and add them all at once -// TODO this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly +// we need to collect all the background blocks and add them all at once // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const char *s; PangoAttrList *attrs; - // keys are pointers to size_t maintained by g_new0()/g_free() - // values are strings - GHashTable *features; // TODO use pango's built-in background attribute? GPtrArray *backgroundClosures; }; -static gboolean featurePosEqual(gconstpointer a, gconstpointer b) +// TODO merge this into the main function below +static PangoAttribute *mkFeaturesAttribute(const uiOpenTypeFeatures *otf) { - size_t *sa = (size_t *) a; - size_t *sb = (size_t *) b; + char *s; + PangoAttribute *attr; - return *sa == *sb; -} - -static guint featurePosHash(gconstpointer n) -{ - size_t *sn = (size_t *) n; - - return (guint) (*sn); -} - -static void freeFeatureString(gpointer s) -{ - g_string_free((GString *) s, TRUE); -} - -#define isCodepointStart(b) (((uint8_t) (b)) <= 0x7F || ((uint8_t) (b)) >= 0xC0) - -static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) -{ - size_t i; - size_t *key; - GString *new; - - new = otfToPangoCSSString(otf); - for (i = start; i < end; i++) { - // don't create redundant entries; see below - if (!isCodepointStart(p->s[i])) - continue; - key = g_new0(size_t, 1); - *key = i; - g_hash_table_replace(p->features, key, g_strdup(new->str)); - } - g_string_free(new, TRUE); + s = otfToPangoCSSString(otf); + attr = FUTURE_pango_attr_font_features_new(s); + g_free(s); + return attr; } struct closureParams { @@ -199,8 +167,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - // TODO ensure the parentheses around spec->Value are provided on Windows (TODO still relevant?) - setFeaturesInRange(p, start, end, spec->Features); + // TODO handle NULLs properly on all platforms + addattr(p, start, end, mkFeaturesAttribute(spec->Features)); break; default: // TODO complain @@ -209,29 +177,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } -static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) -{ - struct foreachParams *p = (struct foreachParams *) data; - size_t *pos = (size_t *) key; - char *s = (char *) value; - size_t n; - - // make sure we cover an entire code point - // otherwise Pango will break apart multi-byte characters, spitting out U+FFFD characters at the invalid points - n = 1; - while (!isCodepointStart(p->s[*pos + n])) - n++; - addattr(p, *pos, *pos + n, - FUTURE_pango_attr_font_features_new(s)); - return TRUE; // always delete; we're emptying the map -} - -static void applyAndFreeFeatureAttributes(struct foreachParams *p) -{ - g_hash_table_foreach_remove(p->features, applyFeatures, p); - g_hash_table_destroy(p->features); -} - static void unrefClosure(gpointer data) { g_closure_unref((GClosure *) data); @@ -243,12 +188,8 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **bac fep.s = uiAttributedStringString(p->String); fep.attrs = pango_attr_list_new(); - fep.features = g_hash_table_new_full( - featurePosHash, featurePosEqual, - g_free, g_free); fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - applyAndFreeFeatureAttributes(&fep); *backgroundClosures = fep.backgroundClosures; return fep.attrs; } diff --git a/unix/opentype.c b/unix/opentype.c index f9d14e09..608a0815 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -187,7 +187,7 @@ static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) return 0; } -GString *otfToPangoCSSString(uiOpenTypeFeatures *otf) +gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf) { GString *s; @@ -196,5 +196,5 @@ GString *otfToPangoCSSString(uiOpenTypeFeatures *otf) if (s->len != 0) // and remove the last comma g_string_truncate(s, s->len - 2); - return s; + return g_string_free(s, FALSE); } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index c80e8cc6..46a2426f 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -73,4 +73,4 @@ extern const PangoStyle pangoItalics[]; extern const PangoStretch pangoStretches[]; // opentype.c -extern GString *otfToPangoCSSString(uiOpenTypeFeatures *otf); +extern gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf); From 1d40ab659c9004e4da33720c842d2c5d5aa13fb3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 19:07:59 -0400 Subject: [PATCH 242/487] More TODOs. --- ui_attrstr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 0d1b9d81..2f41cebb 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -34,6 +34,7 @@ _UI_ENUM(uiAttribute) { uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO note these are copied + // TODO figure out how we're going to deal with being able to edit features over time... uiAttributeFeatures, // use Features }; From 1e31ef24c6dec9b72b8ea9b900a74d2d64ba36a8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 22:22:56 -0400 Subject: [PATCH 243/487] Minor code cleanup. Not sure what to do next... --- unix/attrstr.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index ad620aed..7f12fff8 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -3,27 +3,13 @@ // TODO pango alpha attributes turn 0 into 65535 :| -// we need to collect all the background blocks and add them all at once -// TODO rename this struct to something that isn't exclusively foreach-ing? +// TODO make this name less generic? struct foreachParams { - const char *s; PangoAttrList *attrs; // TODO use pango's built-in background attribute? GPtrArray *backgroundClosures; }; -// TODO merge this into the main function below -static PangoAttribute *mkFeaturesAttribute(const uiOpenTypeFeatures *otf) -{ - char *s; - PangoAttribute *attr; - - s = otfToPangoCSSString(otf); - attr = FUTURE_pango_attr_font_features_new(s); - g_free(s); - return attr; -} - struct closureParams { size_t start; size_t end; @@ -63,7 +49,7 @@ static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double p->g = g; p->b = b; p->a = a; - closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); + closure = g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); // TODO write a specific marshaler // TODO or drop the closure stuff entirely g_closure_set_marshal(closure, g_cclosure_marshal_generic); @@ -84,6 +70,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; PangoUnderline underline; + char *featurestr; switch (spec->Type) { case uiAttributeFamily: @@ -168,7 +155,10 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; case uiAttributeFeatures: // TODO handle NULLs properly on all platforms - addattr(p, start, end, mkFeaturesAttribute(spec->Features)); + featurestr = otfToPangoCSSString(spec->Features); + addattr(p, start, end, + FUTURE_pango_attr_font_features_new(featurestr)); + g_free(featurestr); break; default: // TODO complain @@ -186,7 +176,6 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **bac { struct foreachParams fep; - fep.s = uiAttributedStringString(p->String); fep.attrs = pango_attr_list_new(); fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); From b47431cd7c6f00370c3f29143db5f4aab0f6e15f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 22:57:38 -0400 Subject: [PATCH 244/487] More TODOs. --- windows/separator.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/windows/separator.cpp b/windows/separator.cpp index e123e275..28e77e83 100644 --- a/windows/separator.cpp +++ b/windows/separator.cpp @@ -1,6 +1,10 @@ // 20 may 2015 #include "uipriv_windows.hpp" +// TODO +// - font scaling issues? https://www.viksoe.dk/code/bevelline.htm +// - isn't something in vista app guidelines suggesting this too? or some other microsoft doc? and what about VS itself? + // references: // - http://stackoverflow.com/questions/2892703/how-do-i-draw-separators // - https://msdn.microsoft.com/en-us/library/windows/desktop/dn742405%28v=vs.85%29.aspx From 98459d28787e92621cd6c1ffa24a9f98a2cb0f22 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 18:45:11 -0400 Subject: [PATCH 245/487] Dropped the redundant features collection stuff on Windows like we did on OS X and GTK+ yesterday. That just leaves all the drawing effects, which we have to collect in a different way (like we did on OS X). --- windows/attrstr.cpp | 50 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index be23961e..91475602 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -5,7 +5,7 @@ // TODO this whole file needs cleanup // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() -// we also need to collect all the OpenType features and background blocks and add them all at once +// we also need to collect all the background blocks and add them all at once // TODO(TODO does not seem to apply here?) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? @@ -13,7 +13,6 @@ struct foreachParams { const uint16_t *s; IDWriteTextLayout *layout; std::map *effects; - std::map *features; std::vector *backgroundFuncs; }; @@ -27,6 +26,7 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e for (i = start; i < end; i++) { // don't create redundant entries; see below + // TODO explain this more clearly (surrogate pairs) if (!isCodepointStart(p->s[i])) continue; t = (*(p->effects))[i]; @@ -40,24 +40,6 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e } } -static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, const uiOpenTypeFeatures *otf) -{ - IDWriteTypography *dt; - size_t i; - - dt = otfToDirectWrite(otf); - for (i = start; i < end; i++) { - // don't create redundant entries; see below - // TODO explain this more clearly (surrogate pairs) - if (!isCodepointStart(p->s[i])) - continue; - dt->AddRef(); - (*(p->features))[i] = dt; - } - // and release the initial reference - dt->Release(); -} - static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) { return [=](uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { @@ -79,6 +61,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; + IDWriteTypography *dt; HRESULT hr; ostart = start; @@ -196,7 +179,11 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - setFeaturesInRange(p, start, end, spec->Features); + dt = otfToDirectWrite(spec->Features); + hr = p->layout->SetTypography(dt, range); + if (hr != S_OK) + logHRESULT(L"error applying features attribute", hr); + dt->Release(); break; default: // TODO complain @@ -224,25 +211,6 @@ static void applyAndFreeEffectsAttributes(struct foreachParams *p) delete p->effects; } -static void applyAndFreeFeatureAttributes(struct foreachParams *p) -{ - DWRITE_TEXT_RANGE range; - HRESULT hr; - - for (auto iter = p->features->begin(); iter != p->features->end(); iter++) { - // make sure we cover an entire code point - range.startPosition = iter->first; - range.length = 1; - if (!isCodepointStart(p->s[iter->first])) - range.length = 2; - hr = p->layout->SetTypography(iter->second, range); - if (hr != S_OK) - logHRESULT(L"error applying typographic features attributes", hr); - iter->second->Release(); - } - delete p->features; -} - void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) { struct foreachParams fep; @@ -250,10 +218,8 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou fep.s = attrstrUTF16(p->String); fep.layout = layout; fep.effects = new std::map; - fep.features = new std::map; fep.backgroundFuncs = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeEffectsAttributes(&fep); - applyAndFreeFeatureAttributes(&fep); *backgroundFuncs = fep.backgroundFuncs; } From 2118259769d4ea7a2fb5a38cee4a0c71feb3c6a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 20:33:40 -0400 Subject: [PATCH 246/487] Set up text effect stuff. --- windows/draw.hpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/windows/draw.hpp b/windows/draw.hpp index f3a8d387..8cfbbf72 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -1,5 +1,7 @@ // 5 may 2016 +// TODO resolve overlap between this and the other hpp files (some functions leaked into uipriv_windows.hpp) + // draw.cpp extern ID2D1Factory *d2dfactory; struct uiDrawContext { @@ -20,6 +22,7 @@ typedef std::function **backgroundFuncs); // drawtext.cpp +// TODO reconcile this with attrstr.cpp class textDrawingEffect : public IUnknown { ULONG refcount; public: @@ -76,6 +79,50 @@ public: return this->refcount; } + // TODO deduplicate this with common/attrlist.c + bool same(textDrawingEffect *b) + { + static auto boolsDiffer = [](bool a, bool b) -> bool { + if (a && b) + return false; + if (!a && !b) + return false; + return true; + }; + + if (boolsDiffer(this->hasColor, b->hasColor)) + return false; + if (this->hasColor) { + // TODO use a closest match? + if (this->r != b->r) + return false; + if (this->g != b->g) + return false; + if (this->b != b->b) + return false; + if (this->a != b->a) + return false; + } + if (boolsDiffer(this->hasUnderline, b->hasUnderline)) + return false; + if (this->hasUnderline) + if (this->u != b->u) + return false; + if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) + return false; + if (this->hasUnderlineColor) { + // TODO use a closest match? + if (this->ur != b->ur) + return false; + if (this->ug != b->ug) + return false; + if (this->ub != b->ub) + return false; + if (this->ua != b->ua) + return false; + } + return true; + } }; // TODO these should not be exported extern std::map dwriteItalics; From 933a8f592ac7ccaaacd4eccf960f8c46622837ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 22:24:34 -0400 Subject: [PATCH 247/487] And cleaned up the Windows drawing effect code like we did yesterday. --- windows/attrstr.cpp | 57 ++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 91475602..1552deb8 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -6,29 +6,24 @@ // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() // we also need to collect all the background blocks and add them all at once -// TODO(TODO does not seem to apply here?) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; + size_t len; IDWriteTextLayout *layout; std::map *effects; std::vector *backgroundFuncs; }; -#define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) - static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function f) { size_t i; size_t *key; textDrawingEffect *t; + // TODO explain why we make one for every character for (i = start; i < end; i++) { - // don't create redundant entries; see below - // TODO explain this more clearly (surrogate pairs) - if (!isCodepointStart(p->s[i])) - continue; t = (*(p->effects))[i]; if (t != NULL) { f(t); @@ -194,20 +189,49 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t static void applyAndFreeEffectsAttributes(struct foreachParams *p) { + size_t i, n; + textDrawingEffect *effect, *effectb; DWRITE_TEXT_RANGE range; - HRESULT hr; + auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { + HRESULT hr; - for (auto iter = p->effects->begin(); iter != p->effects->end(); iter++) { - // make sure we cover an entire code point - range.startPosition = iter->first; - range.length = 1; - if (!isCodepointStart(p->s[iter->first])) - range.length = 2; - hr = p->layout->SetDrawingEffect(iter->second, range); + if (effect == NULL) + return; + hr = layout->SetDrawingEffect(effect, range); if (hr != S_OK) logHRESULT(L"error applying drawing effects attributes", hr); - iter->second->Release(); + effect->Release(); + }; + + // go through, fililng in the effect attribute for successive ranges of identical textDrawingEffects + // we are best off treating series of identical effects as single ranges ourselves for parity across platforms, even if Windows does something similar itself + // this also avoids breaking apart surrogate pairs (though IIRC Windows doing the something similar itself might make this a non-issue here) + effect = NULL; + n = p->len; + range.startPosition = 0; + for (i = 0; i < n; i++) { + effectb = (*(p->effects))[i]; + // run of no effect? + if (effect == NULL && effectb == NULL) + continue; + // run of the same effect? + if (effect != NULL && effectb != NULL) + if (effect->same(effectb)) { + effectb->Release(); + continue; + } + + // the effect has changed; commit the old effect + range.length = i - range.startPosition; + apply(p->layout, effect, range); + + range.startPosition = i; + effect = effectb; } + // and apply the last effect + range.length = i - range.startPosition; + apply(p->layout, effect, range); + delete p->effects; } @@ -216,6 +240,7 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou struct foreachParams fep; fep.s = attrstrUTF16(p->String); + fep.len = attrstrUTF16Len(p->String); fep.layout = layout; fep.effects = new std::map; fep.backgroundFuncs = new std::vector; From 8cacd0ba03c5f661051c53afe44a9fb6976a6210 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 22:25:10 -0400 Subject: [PATCH 248/487] Quick fixup. --- windows/attrstr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 1552deb8..71f753d4 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -192,7 +192,7 @@ static void applyAndFreeEffectsAttributes(struct foreachParams *p) size_t i, n; textDrawingEffect *effect, *effectb; DWRITE_TEXT_RANGE range; - auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { + static auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { HRESULT hr; if (effect == NULL) From 96d06121c8b8a8696dcced378df02c4eeb95cd74 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 1 Jun 2017 10:57:34 -0400 Subject: [PATCH 249/487] And finally used OpenType features directly on supported OS X versions. --- darwin/opentype.m | 50 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 00a76200..56bdc755 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -90,7 +90,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature // TODO explain all this // TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) // TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void *data) +static int otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; @@ -125,14 +125,60 @@ static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void return 0; } +// TODO find out which fonts differ in AAT small caps and test them with this +static int otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) +{ + CFMutableArrayRef outerArray = (CFMutableArrayRef) data; + CFDictionaryRef innerDict; + // TODO rename this to tagstr (and all the other variables likewise...) + CFStringRef strTag; + CFNumberRef numValue; + char tagcstr[5]; + const void *keys[2], *values[2]; + + tagcstr[0] = a; + tagcstr[1] = b; + tagcstr[2] = c; + tagcstr[3] = d; + tagcstr[4] = '\0'; + keys[0] = *FUTURE_kCTFontOpenTypeFeatureTag; + keys[1] = *FUTURE_kCTFontOpenTypeFeatureValue; + strTag = CFStringCreateWithCString(NULL, tagcstr, kCFStringEncodingUTF8); + if (strTag == NULL) { + // TODO + } + numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, + (const SInt32 *) (&value)); + values[0] = strTag; + values[1] = numValue; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numValue); + CFRelease(strTag); + // TODO + return 0; +} + CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) { CFMutableArrayRef outerArray; + uiOpenTypeFeaturesForEachFunc f; outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (outerArray == NULL) { // TODO } - uiOpenTypeFeaturesForEach(otf, otfArrayForEach, outerArray); + f = otfArrayForEachAAT; + if (FUTURE_kCTFontOpenTypeFeatureTag != NULL && FUTURE_kCTFontOpenTypeFeatureValue != NULL) + f = otfArrayForEachOT; + uiOpenTypeFeaturesForEach(otf, f, outerArray); return outerArray; } From 18d67a016c6c5e50b1af9f14720ed307678e3db2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 2 Jun 2017 23:57:40 -0400 Subject: [PATCH 250/487] Started work on OS X 10.12 API stupids. --- darwin/sierra.h | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 darwin/sierra.h diff --git a/darwin/sierra.h b/darwin/sierra.h new file mode 100644 index 00000000..1a76cb76 --- /dev/null +++ b/darwin/sierra.h @@ -0,0 +1,79 @@ +// 2 june 2017 + +// The OS X 10.12 SDK introduces a number of new names for +// existing constants to align the naming conventions of +// Objective-C and Swift (particularly in AppKit). +// +// Unfortunately, in a baffling move, instead of using the existing +// AvailabilityMacros.h method of marking things deprecated, they +// rewrote the relevant constants in ways that make +// source-compatible building much more annoying: +// +// - The replacement names are now the only names in the enum +// or define sets they used to be in. +// - The old names are provided as static const variables, which +// means any code that used the old names in a switch case now +// spit out a compiler warning in strict C99 mode (TODO and in C++ mode?). +// - The old names are marked with new deprecated-symbol +// macros that are *not* compatible with the AvailabilityMacros.h +// macros, meaning their deprecation warnings still come +// through. (It should be noted that AvailabilityMacros.h was still +// updated for 10.12 regardless, hence our #ifdef below.) +// +// As far as I can gather, these facts are not documented *at all*, so +// in the meantime, other open-source projects just use their own +// #defines to maintain source compatibility, either by making the +// new names available everywhere or the old ones un-deprecated. +// We choose the latter. +// TODO file a radar on the issue (after determining C++ compatibility) so this can be pinned down once and for all +// TODO after that, link my stackoverflow question here too +// TODO make sure this #ifdef does actually work on older systems + +#ifdef MAC_OS_X_VERSION_10_12 + +#define NSControlKeyMask NSEventModifierFlagControl +#define NSAlternateKeyMask NSEventModifierFlagOption +#define NSShiftKeyMask NSEventModifierFlagShift +#define NSCommandKeyMask NSEventModifierFlagCommand + +#define NSLeftMouseDown NSEventTypeLeftMouseDown +#define NSRightMouseDown NSEventTypeRightMouseDown +#define NSOtherMouseDown NSEventTypeOtherMouseDown +#define NSLeftMouseUp NSEventTypeLeftMouseUp +#define NSRightMouseUp NSEventTypeRightMouseUp +#define NSOtherMouseUp NSEventTypeOtherMouseUp +#define NSLeftMouseDragged NSEventTypeLeftMouseDragged +#define NSRightMouseDragged NSEventTypeRightMouseDragged +#define NSOtherMouseDragged NSEventTypeOtherMouseDragged +#define NSKeyDown NSEventTypeKeyDown +#define NSKeyUp NSEventTypeKeyUp +#define NSFlagsChanged NSEventTypeFlagsChanged +#define NSApplicationDefined NSEventTypeApplicationDefined +#define NSPeriodic NSEventTypePeriodic +#define NSMouseMoved NSEventTypeMouseMoved + +#define NSRegularControlSize NSControlSizeRegular +#define NSSmallControlSize NSControlSizeSmall + +#define NSAnyEventMask NSEventMaskAny +#define NSLeftMouseDraggedMask NSEventMaskLeftMouseDragged +#define NSLeftMouseUpMask NSEventMaskLeftMouseUp + +#define NSTickMarkAbove NSTickMarkPositionAbove + +#define NSLinearSlider NSSliderTypeLinear + +#define NSInformationalAlertStyle NSAlertStyleInformational +#define NSCriticalAlertStyle NSAlertStyleCritical + +#define NSBorderlessWindowMask NSWindowStyleMaskBorderless +#define NSTitledWindowMask NSWindowStyleMaskTitled +#define NSClosableWindowMask NSWindowStyleMaskClosable +#define NSMiniaturizableWindowMask NSWindowStyleMaskMiniaturizable +#define NSResizableWindowMask NSWindowStyleMaskResizable + +#endif + +// TODO /Users/pietro/src/github.com/andlabs/libui/darwin/stddialogs.m:83:15: warning: 'beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:' is deprecated: first deprecated in macOS 10.10 - Use -beginSheetModalForWindow:completionHandler: instead [-Wdeprecated-declarations] + +// TODO https://developer.apple.com/library/content/releasenotes/Miscellaneous/RN-Foundation-OSX10.12/ From aa455be1aed1063f3fec9433757f0a3323b66b3c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Jun 2017 00:33:40 -0400 Subject: [PATCH 251/487] Sigh, cmake... --- CMakeLists.txt | 6 +++++- darwin/uipriv_darwin.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f189e2b9..fbcb3358 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,11 @@ cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) # - switch to 3.1.0 features # the docs say we need to set this up prior to project() -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") +# the docs don't say this, but the CACHE part is important; see https://stackoverflow.com/questions/34208360/cmake-seems-to-ignore-cmake-osx-deployment-target +# TODO figure out what other variables must be set with CACHE +# TODO figure out if FORCE is needed here +# TODO figure out whether STRING "" is best or if something else is better; also what FORCE does because I forget and later I say it's needed +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8" CACHE STRING "" FORCE) # we want to disable incremental linking # see also: diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 56a576d4..26366520 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -1,4 +1,5 @@ // 6 january 2015 +// note: as of OS X Sierra, the -mmacosx-version-min compiler options governs deprecation warnings; keep these around anyway just in case #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8 #import From 8c850a26b2d353f631e36885414f31fb3032353a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Jun 2017 17:53:10 -0400 Subject: [PATCH 252/487] More TODOs. --- TODO.md | 3 +++ common/areaevents.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index 8bf48004..41184b93 100644 --- a/TODO.md +++ b/TODO.md @@ -170,3 +170,6 @@ FONT LOADING [01:15:53] FcConfigAppFontAddFile() <-- that API [01:16:30] great, and they don't say what version the API was added in teh docs function: ide_editor_map_bin_add() + +- Mouse ClickLock: do we need to do anything special? *should* we? https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx +- consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553 diff --git a/common/areaevents.c b/common/areaevents.c index cf3c288c..189673a2 100644 --- a/common/areaevents.c +++ b/common/areaevents.c @@ -10,6 +10,8 @@ For GTK+, we pull the double-click time and double-click distance, which work th On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms. Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+. + +TODO note the bits about asymmetry and g_rcClick initial value not mattering in the oldnewthing article */ // x, y, xdist, ydist, and c.rect must have the same units From 8e8cc12105e2db6ff0291d28a0d2acde1c28ec08 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 12:47:07 -0400 Subject: [PATCH 253/487] Added uiForEach for canonicalizing foreach function returns and used it everywhere. --- common/attrlist.c | 10 ++++++---- darwin/attrstr.m | 4 ++-- darwin/opentype.m | 17 +++++++++-------- ui.h | 6 ++++++ ui_attrstr.h | 5 ++--- unix/attrstr.c | 4 ++-- unix/opentype.c | 13 +++++++------ windows/attrstr.cpp | 4 ++-- windows/opentype.cpp | 6 ++++-- 9 files changed, 40 insertions(+), 29 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index e50ec141..cb0833bc 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -651,12 +651,14 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; + uiForEach ret; - for (a = alist->first; a != NULL; a = a->next) - // TODO document this - // TODO should this be return 0 to break? - if ((*f)(s, &(a->spec), a->start, a->end, data)) + for (a = alist->first; a != NULL; a = a->next) { + ret = (*f)(s, &(a->spec), a->start, a->end, data); + if (ret == uiForEachStop) + // TODO for all: break or return? break; + } } struct attrlist *attrlistNew(void) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 6cf412a4..63539702 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -209,7 +209,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; @@ -316,7 +316,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t // TODO complain ; } - return 0; + return uiForEachContinue; } static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) diff --git a/darwin/opentype.m b/darwin/opentype.m index 56bdc755..24f35f63 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -70,15 +70,18 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures NSNumber *vn = (NSNumber *) value; uint32_t tag; uint8_t a, b, c, d; + uiForEach ret; tag = mapObjectValue(tn); a = (uint8_t) ((tag >> 24) & 0xFF); b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - // TODO handle return value - (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)((char) a, (char) b, (char) c, (char) d, mapObjectValue(vn), data); + // TODO for all: require exact match? + if (ret == uiForEachStop) + *stop = YES; }]; } @@ -90,7 +93,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature // TODO explain all this // TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) // TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static int otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; @@ -121,12 +124,11 @@ static int otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, vo CFRelease(numSelector); CFRelease(numType); }); - // TODO - return 0; + return uiForEachContinue; } // TODO find out which fonts differ in AAT small caps and test them with this -static int otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; CFDictionaryRef innerDict; @@ -163,8 +165,7 @@ static int otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, voi CFRelease(innerDict); CFRelease(numValue); CFRelease(strTag); - // TODO - return 0; + return uiForEachContinue; } CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) diff --git a/ui.h b/ui.h index 922d0625..72e5eaef 100644 --- a/ui.h +++ b/ui.h @@ -34,6 +34,12 @@ extern "C" { // TODO uiBool? +// uiForEach represents the return value from one of libui's various ForEach functions. +_UI_ENUM(uiForEach) { + uiForEachContinue, + uiForEachStop, +}; + typedef struct uiInitOptions uiInitOptions; struct uiInitOptions { diff --git a/ui_attrstr.h b/ui_attrstr.h index 2f41cebb..9dc72a5a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -55,7 +55,7 @@ _UI_ENUM(uiDrawUnderlineColor) { // TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? -typedef int (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); // TODO detailed constructor? _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); @@ -83,9 +83,8 @@ struct uiAttributeSpec { const uiOpenTypeFeatures *Features; // TODO rename to OpenTypeFeatures? }; -// TODO name the foreach return values // TODO make the spec const in a way that doesn't allow fields to be modified? -typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from diff --git a/unix/attrstr.c b/unix/attrstr.c index 7f12fff8..7dc83a6e 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -65,7 +65,7 @@ static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttr pango_attr_list_insert(p->attrs, attr); } -static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; @@ -164,7 +164,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t // TODO complain ; } - return 0; + return uiForEachContinue; } static void unrefClosure(gpointer data) diff --git a/unix/opentype.c b/unix/opentype.c index 608a0815..9b90d0ba 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -78,7 +78,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char struct otfForEach { uiOpenTypeFeaturesForEachFunc f; void *data; - // TODO store continuation status here + uiForEach ret; }; static void foreach(gpointer key, gpointer value, gpointer data) @@ -87,13 +87,15 @@ static void foreach(gpointer key, gpointer value, gpointer data) uint32_t tag; uint8_t a, b, c, d; + // we can't stop early, so just ignore the rest if we have to + if (ofe->ret == uiForEachStop) + return; tag = GPOINTER_TO_INT(key); a = (uint8_t) ((tag >> 24) & 0xFF); b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - // TODO handle return value - (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); + ofe->ret = (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) @@ -175,7 +177,7 @@ out: // see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings // TODO make this a g_hash_table_foreach() function (which requires duplicating code)? -static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach toCSS(char a, char b, char c, char d, uint32_t value, void *data) { // TODO is there a G_STRING()? GString *s = (GString *) data; @@ -183,8 +185,7 @@ static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) // the last trailing comma is removed after foreach is done g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", a, b, c, d, value); - // TODO use this - return 0; + return uiForEachContinue; } gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 71f753d4..7bcd5e53 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -49,7 +49,7 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; @@ -184,7 +184,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t // TODO complain ; } - return 0; + return uiForEachContinue; } static void applyAndFreeEffectsAttributes(struct foreachParams *p) diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 0275d916..21833e2c 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -64,14 +64,16 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures end = otf->tags->end(); for (iter = otf->tags->begin(); iter != end; iter++) { uint8_t a, b, c, d; + uiForEach ret; a = (uint8_t) (iter->first & 0xFF); b = (uint8_t) ((iter->first >> 8) & 0xFF); c = (uint8_t) ((iter->first >> 16) & 0xFF); d = (uint8_t) ((iter->first >> 24) & 0xFF); - // TODO handle return value - (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)((char) a, (char) b, (char) c, (char) d, iter->second, data); + if (ret == uiForEachStop) + return; } } From a415a846f76490e902d1382e7f3cd5fe73242284 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 13:46:54 -0400 Subject: [PATCH 254/487] Some comment fixups. --- ui_attrstr.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9dc72a5a..096267ea 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -2,8 +2,9 @@ // optionally be embellished with formatting attributes. libui // provides the list of formatting attributes, which cover common // formatting traits like boldface and color as well as advanced -// typographical features like superscripts and small caps. -// These attributes can be combined in a variety of ways. +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. // // In addition, uiAttributedString provides facilities for moving // between grapheme clusters, which represent a character @@ -18,7 +19,6 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; -// Note: where you say "1 = on", any nonzero value means "on". (TODO) // TODO just make a separate field for everything? _UI_ENUM(uiAttribute) { uiAttributeFamily, // use Family From b3e3b27f717901550c61c32ad35cb85651efcd39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 14:14:33 -0400 Subject: [PATCH 255/487] Some more TODO cleanup and name adjustment. --- darwin/attrstr.m | 6 +++--- examples/drawtext/attributes.c | 2 +- examples/drawtext/basic.c | 2 +- examples/drawtext/emptystr_hittest.c | 4 ++-- examples/drawtext/hittest.c | 4 ++-- ui_attrstr.h | 24 +++++++++++------------- unix/drawtext.c | 6 +++--- windows/drawtext.cpp | 8 ++++---- 8 files changed, 27 insertions(+), 29 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 63539702..c3b8b0b3 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -386,9 +386,9 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) } static const CTTextAlignment ctaligns[] = { - [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, - [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, - [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, + [uiDrawTextAlignLeft] = kCTTextAlignmentLeft, + [uiDrawTextAlignCenter] = kCTTextAlignmentCenter, + [uiDrawTextAlignRight] = kCTTextAlignmentRight, }; static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index f15ba070..ed3cb973 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -906,7 +906,7 @@ struct example *mkAttributesExample(void) setupAttributedString(); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &attributesExample; } diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index 1ddbbe6c..ede973b3 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -259,7 +259,7 @@ struct example *mkBasicExample(void) attrstr = uiNewAttributedString(text); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &basicExample; } diff --git a/examples/drawtext/emptystr_hittest.c b/examples/drawtext/emptystr_hittest.c index 5542e469..fba386d8 100644 --- a/examples/drawtext/emptystr_hittest.c +++ b/examples/drawtext/emptystr_hittest.c @@ -201,7 +201,7 @@ static void changeFont(uiFontButton *b, void *data) static void changeTextAlign(uiCombobox *c, void *data) { // note the order of the items added below - params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + params.Align = (uiDrawTextAlign) uiComboboxSelected(textAlign); redraw(); } @@ -248,7 +248,7 @@ struct example *mkEmptyStringExample(void) attrstr = uiNewAttributedString(text); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &hitTestExample; } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index f5252388..ce0685d1 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -210,7 +210,7 @@ static void changeFont(uiFontButton *b, void *data) static void changeTextAlign(uiCombobox *c, void *data) { // note the order of the items added below - params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + params.Align = (uiDrawTextAlign) uiComboboxSelected(textAlign); redraw(); } @@ -257,7 +257,7 @@ struct example *mkHitTestExample(void) attrstr = uiNewAttributedString(text); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &hitTestExample; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 096267ea..fef4f26f 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -34,7 +34,8 @@ _UI_ENUM(uiAttribute) { uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO note these are copied - // TODO figure out how we're going to deal with being able to edit features over time... + // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + // (TODO related to above: what about memory?) uiAttributeFeatures, // use Features }; @@ -52,11 +53,9 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; -// TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); -// TODO detailed constructor? _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); // TODO put above Free? @@ -80,10 +79,10 @@ struct uiAttributeSpec { double G; double B; double A; - const uiOpenTypeFeatures *Features; // TODO rename to OpenTypeFeatures? + const uiOpenTypeFeatures *Features; }; -// TODO make the spec const in a way that doesn't allow fields to be modified? +// TODO how would we make spec const in this case, to prevent fields from being modified? typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor @@ -139,7 +138,6 @@ _UI_ENUM(uiDrawTextItalic) { uiDrawTextItalicItalic, }; -// TODO realign this so that Normal == 0? _UI_ENUM(uiDrawTextStretch) { uiDrawTextStretchUltraCondensed, uiDrawTextStretchExtraCondensed, @@ -164,18 +162,17 @@ typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; -// TODO drop the Layout from this? -_UI_ENUM(uiDrawTextLayoutAlign) { - uiDrawTextLayoutAlignLeft, - uiDrawTextLayoutAlignCenter, - uiDrawTextLayoutAlignRight, +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, }; struct uiDrawTextLayoutParams { uiAttributedString *String; uiDrawFontDescriptor *DefaultFont; double Width; - uiDrawTextLayoutAlign Align; + uiDrawTextAlign Align; }; // Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. @@ -253,10 +250,11 @@ _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking +// TODO allow secondary carets typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once? +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); diff --git a/unix/drawtext.c b/unix/drawtext.c index 6acb848a..373702cd 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -95,9 +95,9 @@ static void computeLineMetrics(uiDrawTextLayout *tl) } static const PangoAlignment pangoAligns[] = { - [uiDrawTextLayoutAlignLeft] = PANGO_ALIGN_LEFT, - [uiDrawTextLayoutAlignCenter] = PANGO_ALIGN_CENTER, - [uiDrawTextLayoutAlignRight] = PANGO_ALIGN_RIGHT, + [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, + [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, + [uiDrawTextAlignRight] = PANGO_ALIGN_RIGHT, }; uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 275420da..f173f471 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -134,10 +134,10 @@ static void computeLineInfo(uiDrawTextLayout *tl) } // TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -static std::map dwriteAligns = { - { uiDrawTextLayoutAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, - { uiDrawTextLayoutAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, - { uiDrawTextLayoutAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, +static std::map dwriteAligns = { + { uiDrawTextAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, + { uiDrawTextAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, + { uiDrawTextAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, }; uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) From ddf91df7644d01833a1f9d041cdd2cf23f6cbed0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 15:32:51 -0400 Subject: [PATCH 256/487] More cmake TODOs. --- darwin/CMakeLists.txt | 1 + unix/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 0591643b..ef1d30b1 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -59,6 +59,7 @@ if(NOT BUILD_SHARED_LIBS) set(_LIBUINAME libui-temporary PARENT_SCOPE) endif() # thanks to Mr-Hide in irc.freenode.net/#cmake +# TODO remove all these temporary files after linking the final archive file macro(_handle_static) set_target_properties(${_LIBUINAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index f21256c3..c20cf266 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -57,6 +57,7 @@ set(_LIBUINAME libui PARENT_SCOPE) if(NOT BUILD_SHARED_LIBS) set(_LIBUINAME libui-temporary PARENT_SCOPE) endif() +# TODO remove all these temporary files after linking the final archive file macro(_handle_static) set_target_properties(${_LIBUINAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") From ef3ed04e2d9bdbe821e51aaa27fd9804e218e46e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 16:19:08 -0400 Subject: [PATCH 257/487] More hacking to fix visibility issues on GTK+. This is a mess. --- ui_unix.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui_unix.h b/ui_unix.h index 5a91257b..23269186 100644 --- a/ui_unix.h +++ b/ui_unix.h @@ -16,6 +16,7 @@ struct uiUnixControl { uiControl c; uiControl *parent; gboolean addedBefore; + gboolean explicitlyHidden; void (*SetContainer)(uiUnixControl *, GtkContainer *, gboolean); }; #define uiUnixControl(this) ((uiUnixControl *) (this)) @@ -58,11 +59,13 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool #define uiUnixControlDefaultShow(type) \ static void type ## Show(uiControl *c) \ { \ + /*TODO part of massive hack about hidden before*/uiUnixControl(c)->explicitlyHidden=FALSE; \ gtk_widget_show(type(c)->widget); \ } #define uiUnixControlDefaultHide(type) \ static void type ## Hide(uiControl *c) \ { \ + /*TODO part of massive hack about hidden before*/uiUnixControl(c)->explicitlyHidden=TRUE; \ gtk_widget_hide(type(c)->widget); \ } #define uiUnixControlDefaultEnabled(type) \ @@ -86,7 +89,7 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool { \ if (!uiUnixControl(c)->addedBefore) { \ g_object_ref_sink(type(c)->widget); /* our own reference, which we release in Destroy() */ \ - gtk_widget_show(type(c)->widget); \ + /*TODO*/if(!uiUnixControl(c)->explicitlyHidden) gtk_widget_show(type(c)->widget); \ uiUnixControl(c)->addedBefore = TRUE; \ } \ if (remove) \ From dbb17e441db3a8a94b669446764f65527b1c095c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 16:20:58 -0400 Subject: [PATCH 258/487] Comments related to above. --- examples/drawtext/hittest.c | 1 - ui_unix.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index ce0685d1..a8092b47 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -2,7 +2,6 @@ #include "drawtext.h" // TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place -// TODO the hiding and showing does not work properly on GTK+ // TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that // TODO make sure to check the cursor positions of RTL on all platforms diff --git a/ui_unix.h b/ui_unix.h index 23269186..ed019260 100644 --- a/ui_unix.h +++ b/ui_unix.h @@ -89,6 +89,7 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool { \ if (!uiUnixControl(c)->addedBefore) { \ g_object_ref_sink(type(c)->widget); /* our own reference, which we release in Destroy() */ \ + /* massive hack notes: without any of this, nothing gets shown when we show a window; without the if, all things get shown even if some were explicitly hidden (TODO why don't we just show everything except windows on create? */ \ /*TODO*/if(!uiUnixControl(c)->explicitlyHidden) gtk_widget_show(type(c)->widget); \ uiUnixControl(c)->addedBefore = TRUE; \ } \ From 39a8d1a07ef0a932bc9f885f4c2e48c141b464fd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 7 Jun 2017 15:58:11 -0400 Subject: [PATCH 259/487] More TODOs. --- windows/uipriv_windows.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index f5adacdd..3f199cbc 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -7,6 +7,7 @@ #include "compilerver.hpp" // ui internal window messages +// TODO make these either not messages or WM_USER-based, so we can be sane about reserving WM_APP enum { // redirected WM_COMMAND and WM_NOTIFY msgCOMMAND = WM_APP + 0x40, // start offset just to be safe From 20239df6f284b56f231d76af252b0131741ef14e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Jun 2017 00:59:17 -0400 Subject: [PATCH 260/487] Some TODO resolution via documentation writing. --- ui_attrstr.h | 52 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index fef4f26f..9a5e1bfe 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,23 +19,50 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; -// TODO just make a separate field for everything? +// TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { - uiAttributeFamily, // use Family - uiAttributeSize, // use Double + // uiAttributeFamily changes the font family of the text it is + // applied to. Use the Family field of uiAttributeSpec. + // TODO case-sensitive? + uiAttributeFamily, + // uiAttributeSize changes the size of the text it is applied to, + // in typographical points. Use the Double field of + // uiAttributeSpec. + uiAttributeSize, + // uiAttributeWeight changes the weight of the text it is applied + // to. Use the Value field of uiAttributeSpec and the + // uiDrawTextWeight constants. uiAttributeWeight, + // uiAttributeItalic changes the italicness of the text it is applied + // to. Use the Value field of uiAttributeSpec and the + // uiDrawTextItalic constants. uiAttributeItalic, + // uiAttributeStretch changes the stretch of the text it is applied + // to. Use the Value field of uiAttributeSpec and the + // uiDrawTextStretch constants. uiAttributeStretch, - uiAttributeColor, // use R, G, B, A - uiAttributeBackground, // use R, G, B, A + // uiAttributeColor changes the color of the text it is applied to. + // Use the R, G, B, and A fields of uiAttributeSpec. + uiAttributeColor, + // uiAttributeBackground changes the color of the text it is + // applied to. Use the R, G, B, and A fields of uiAttributeSpec. + uiAttributeBackground, - uiAttributeUnderline, // enum uiDrawUnderlineStyle - // TODO document that the color in the case we don't specify it is the text color - uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + // uiAttributeUnderline changes the underline style of the text + // it is applied to. Use the Value field of uiAttributeSpec and the + // uiDrawUnderlineStyle constants. + uiAttributeUnderline, + // uiAttributeUnderlineColor changes the color of any underline + // on the text it is applied to, regardless of the style. Use the + // Value field of uiAttributeSpec and the uiDrawUnderlineColor + // constants (refer to its documentation for more information). + // + // If an underline style is applied but no underline color is + // specified, the text color is used instead. + uiAttributeUnderlineColor, - // TODO note these are copied - // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - // (TODO related to above: what about memory?) + // uiAttributeFeatures changes the OpenType features of the + // text it is applied to. Use the Features field of uiAttributeSpec. uiAttributeFeatures, // use Features }; @@ -70,6 +97,9 @@ _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpen typedef struct uiAttributeSpec uiAttributeSpec; +// TODO note that pointers are copied +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit +// (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) struct uiAttributeSpec { uiAttribute Type; const char *Family; From cde1a201f4bc479d7df744d4948be59de7acfd97 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Jun 2017 15:31:28 -0400 Subject: [PATCH 261/487] Expanded documentation in ui_attrstr.h in an attempt to reduce TODOs. Instead, I added more. :D --- ui_attrstr.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++---- unix/area.c | 3 +++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9a5e1bfe..e5147a02 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,6 +19,7 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; +// TODO either here or above, say that only one attribute can be applied per attribute type per character // TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is @@ -80,19 +81,70 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// The full list of OpenType features is part of the OpenType +// specification: +// https://www.microsoft.com/typography/otspec/featuretags.htm +// Refer to it for information on specific features and how to use +// them. +// TODO reformat this somehow (how do Go packages do things like this?) typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; -// TODO pass the feature set? + +// TODO pass the feature set? (resolve const struct issue below first) typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); -// TODO put above Free? -// TODO Copy instead of Clone? + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. +// TODO what happens if the tag isn't there? _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// TODO zero-fill value unconditionally? and if so, to other functions in libui +// TODO allow NULL for value? and throughout libui? +// TODO const-correct this function (can we do that given the members of the struct on some platforms being full blown objects that may or may not themselves be const-correct?) _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); -// TODO make other enumerators const (and in general const-correct everything) + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. +// TODO make other enumerators const (and in general const-correct everything) (but see the const struct TODO below and the const struct object member TODO above) _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. +// a is defined as equal to b if and only if both +// - contain the same tags, without any extras or missing tags +// either way, and +// - have each tag have the same values +// TODO what if either or both are NULL? _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; diff --git a/unix/area.c b/unix/area.c index ca99e602..24cd9513 100644 --- a/unix/area.c +++ b/unix/area.c @@ -92,6 +92,9 @@ static void areaWidget_size_allocate(GtkWidget *w, GtkAllocation *allocation) if (!a->scrolling) // we must redraw everything on resize because Windows requires it + // TODO https://developer.gnome.org/gtk3/3.10/GtkWidget.html#gtk-widget-set-redraw-on-allocate ? + // TODO drop this rule; it was stupid and documenting this was stupid — let platforms where it matters do it on their own + // TODO or do we not, for parity of performance? gtk_widget_queue_resize(w); } From b73721905faf6d1d959a937f11905b9494194aab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 9 Jun 2017 19:43:55 -0400 Subject: [PATCH 262/487] Meh. --- ui_attrstr.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index e5147a02..c7ab225c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -91,12 +91,14 @@ _UI_ENUM(uiDrawUnderlineColor) { // often used as a Boolean flag, but sometimes as an index to choose // a glyph shape to use. // -// The full list of OpenType features is part of the OpenType -// specification: +// If a font does not support a certain feature, that feature will be +// ignored. +// +// See the OpenType specification at // https://www.microsoft.com/typography/otspec/featuretags.htm -// Refer to it for information on specific features and how to use -// them. -// TODO reformat this somehow (how do Go packages do things like this?) +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? (resolve const struct issue below first) From d63a5b23b1725bd53504f952eca1cd887b685773 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 9 Jun 2017 19:59:48 -0400 Subject: [PATCH 263/487] Handled uiOpenTypeFeatures NULL equality. This only added more TODOs elsewhere :| --- darwin/attrstr.m | 8 +------- darwin/opentype.m | 4 ++++ ui_attrstr.h | 7 ++----- unix/attrstr.c | 2 +- unix/opentype.c | 5 +++++ windows/attrstr.cpp | 1 + windows/opentype.cpp | 4 ++++ 7 files changed, 18 insertions(+), 13 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c3b8b0b3..4d166fb0 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -113,13 +113,7 @@ struct foreachParams { return NO; if (self.stretch != b.stretch) return NO; - // TODO make this part of uiOpenTypeFeaturesEqual() on all platforms - if (self.features == NULL && b.features == NULL) - return YES; - if (self.features != NULL && b.features == NULL) - return NO; - if (self.features == NULL && b.features != NULL) - return NO; + // this also handles NULL cases if (!uiOpenTypeFeaturesEqual(self.features, b.features)) return NO; return YES; diff --git a/darwin/opentype.m b/darwin/opentype.m index 24f35f63..46bdaf19 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -87,6 +87,10 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; return [a->tags isEqualToDictionary:b->tags]; } diff --git a/ui_attrstr.h b/ui_attrstr.h index c7ab225c..b39b39a2 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -142,11 +142,8 @@ _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, ch _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); // uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. -// a is defined as equal to b if and only if both -// - contain the same tags, without any extras or missing tags -// either way, and -// - have each tag have the same values -// TODO what if either or both are NULL? +// a is defined as equal to b if and only if both have exactly the same +// tags with exactly the same values, or if both are NULL. _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; diff --git a/unix/attrstr.c b/unix/attrstr.c index 7dc83a6e..4d384daf 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -154,7 +154,7 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: - // TODO handle NULLs properly on all platforms + // TODO make sure NULLs are handled properly on all platforms in this part of the code featurestr = otfToPangoCSSString(spec->Features); addattr(p, start, end, FUTURE_pango_attr_font_features_new(featurestr)); diff --git a/unix/opentype.c b/unix/opentype.c index 9b90d0ba..79b1d93a 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -133,6 +133,11 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature guint i; int equal = 0; + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + ak = copySortedKeys(a->tags); bk = copySortedKeys(b->tags); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 7bcd5e53..9dceaea6 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -174,6 +174,7 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: + // TODO make sure this behaves properly if spec->Features is NULL dt = otfToDirectWrite(spec->Features); hr = p->layout->SetTypography(dt, range); if (hr != S_OK) diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 21833e2c..89b5913c 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -79,6 +79,10 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; // TODO make sure this is correct return *(a->tags) == *(b->tags); } From f0813ac6e422758c0f778bacc772f328e918a961 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 03:37:17 -0400 Subject: [PATCH 264/487] More stuff. I should probably write that OpenType features test now. --- darwin/attrstr.m | 2 ++ ui_attrstr.h | 2 +- unix/attrstr.c | 5 ++++- windows/attrstr.cpp | 4 +++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 4d166fb0..6975ca06 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -372,6 +372,8 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) font = defaultFont; CFRetain(font); } else + // note that this handles the difference between NULL and empty uiOpenTypeFeatures properly as far as conversion to native data formats is concerned (NULL does not generate a features dictionary; empty just produces an empty one) + // TODO but what about from the OS's perspective on all OSs? font = [cfa toCTFont]; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); diff --git a/ui_attrstr.h b/ui_attrstr.h index b39b39a2..0024442c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -92,7 +92,7 @@ _UI_ENUM(uiDrawUnderlineColor) { // a glyph shape to use. // // If a font does not support a certain feature, that feature will be -// ignored. +// ignored. (TODO verify this on all OSs) // // See the OpenType specification at // https://www.microsoft.com/typography/otspec/featuretags.htm diff --git a/unix/attrstr.c b/unix/attrstr.c index 4d384daf..245ae7f6 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -154,7 +154,10 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: - // TODO make sure NULLs are handled properly on all platforms in this part of the code + // only generate an attribute if spec->Features is not NULL + // TODO state that this is allowed + if (spec->Features == NULL) + break; featurestr = otfToPangoCSSString(spec->Features); addattr(p, start, end, FUTURE_pango_attr_font_features_new(featurestr)); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 9dceaea6..f180f74b 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -174,7 +174,9 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: - // TODO make sure this behaves properly if spec->Features is NULL + // only generate an attribute if spec->Features is not NULL + if (spec->Features == NULL) + break; dt = otfToDirectWrite(spec->Features); hr = p->layout->SetTypography(dt, range); if (hr != S_OK) From 99e65d49a70998eac8c9aaf58c7a614ebdfe93b8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 16:56:17 -0400 Subject: [PATCH 265/487] Added some real contribution guidelines. --- CONTRIBUTING.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++ 2 files changed, 124 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3b8dcda9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,120 @@ +# Contributing to libui + +libui is an open source project that openly accepts contributions. I appreciate your help! + +## Rules for contributing code + +While libui is open to contributions, a number of recent, significantly large contributions and uncontributed forks have recently surfaced that do not present themselves in a form that makes it easy for libui to accept them. In order to give your contribution a high chance of being accepted into libui, please keep the following in mind as you prepare your contribution. + +### Commit messages and pull request description + +libui does not enforce rules about the length or detail that a commit message. I'm not looking for an essay. However, single-word descriptions of nontrivial changes are *not* acceptable. I should be able to get a glimpse of what a commit does from the commit message, even if it's just one sentence to describe a trivial change. (Yes, I know I haven't followed this rule strictly myself, but I try not to break it too.) And a commit message should encompass everything; typically, I make a number of incremental commits toward a feature, so the commit messages don't have to be too long to explain everything. + +Your pull request description, on the other hand, must be a summary of the sum total of all the changes made to libui. Don't just drop a pull request on me with a one-line-long elevator pitch of what you added. Describe your proposed API changes, implementation requirements, and any important consequences of your work. + +### Code formatting + +libui uses K&R C formatting rules for overall code structure: spaces after keywords like `if`, `{` on the same line as a statement with a space, `{` on its own line after a function or method signature (even those inside the class body), no space after the name of a function, etc. + +Use hard tabs, NOT spaces, for indentation. I use a proportional-width font and my text editor doesn't set tabs to a multiple of the space width, so I *will* be able to tell. If you use a fixed-width font, I suggest setting a tab width of 4 spaces per tab, but don't put diagrams in comments with hard tabs, because not everyone does this. + +Expressions should have a spce around binary operators, and use parentheses where it would help humans gather the meaning of an expression, regardless of whether a computer could tell what is correct. + +When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`. + +In the event you are unsure of something, refer to existing libui code for examples. I may wind up fixing minor details later anyway, so don't fret about getting minor details right the first time. + +### Naming + +libui uses camel-case for naming, with a handful of very specific exceptions (namely GObject method names, where GObject itself enforces the naming convention). + +All public API names should begin with `ui` and followed by a capital letter. All public struct names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`. + +No private API name should begin with `ui` followed by a capital letter; in fact, I would ideally like to be able to get away with lax naming for private functions. I may have to change that if it becomes technically difficult to do in the case of static linking later on down the road. (`uiMalloc()`, `uiRealloc()`, and `uiFree()` are grandfathered-in exceptions.) + +Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case name, `HTTP` for all else, but **NEVER** `Http`. This is possibly the only aspect of the controversial nature of code style that I consider indefensibly stupid. + +### API documentation + +(TODO I am writing an API documentation tool; once that becomes stable enough I can talk about documenting libui properly. You'll see vestiges of it throughout ui.h, though.) + +### Compatibility + +libui takes backward compatibility seriously. Your code should not break the current compatibility requirements. All platforms provide a series of macros, defined in the various `uipriv_*.h*` files (or `winapi.hpp` on Windows), that specify the minimum required version. If you find yourself needing to remove these or ignore resultant warnings or errors, you're probably breaking compatibility. + +Choosing to drop older versions of Windows, GTK+, and OS X that I could have easily continued to support was not done lightly. If you want to discuss dropping support for an older version of any of these for the benefit of libui, file an issue pleading your case (see below). + +GTK+ versions are harder to drop because I am limited by Linux distribution packaging. In general, I will consider bumping GTK+ versions on a new Ubuntu LTS release, choosing the earliest version available on the major distributions at the time of the *previous* Ubuntu LTS release. As of writing, the next milestone will be *after* April 2018, and the target GTK+ version appears to be 3.18, judging by Ubuntu 16.04 LTS alone. This may be bumped back depending on other distros (or it may not be bumped at all), but you may wish to keep this in mind as you write. + +(TODO talk about future.c/.cpp/.m files) + +As for language compatibility, libui is written in C99. I have no intention of changing this. + +As for build system compatibility, libui uses CMake 3.1.0. If you wish to bump the version, file an issue pleading your case (but see below). + +**If you do plead your case**, keep in mind that "it's old" is not a sufficient reason to drop things. If you can prove that **virtually no one** uses the minimum version anymore, then that is stronger evidence. The best evidence, however, is that not upgrading will hold libui back in some significant way — but beware that there are some things I won't add to libui itself. + +### Windows-specific notes + +The Windows backend of libui is written in C++ using C++11. + +Despite using C++, please refrain from using the following: + +- using C++ in ui_windows.h (this file should still be C compatible) +- smart pointers +- namespaces +- `using namespace` +- ATL, MFC, WTL + +The following are not recommended, for consistency with the rest of libui: + +- variable declarations anywhere in a function (keep them all at the top) +- `for (int...` (C++11 foreach syntax is fine, though) +- omitting the `struct` on type names for ordinary structs + +The format of a class should be + +```c++ +class name : public ancestor { + // private variables here +public: + // public stuff here +}; +``` + +### GTK+-specific notes + +Avoid GNU-specific language features. I build with strict C99 conformance. + +### OS X-specific notes + +libui is presently **not** ARC-compliant. Features that require ARC should be avoided for now. I may consider changing this in the future, but it will be a significant change. + +To ensure maximum compiler output in the event of a coding error, there should not be any implicit method calls in Objective-C code. For instance, don't do + +```objective-c +[[array objectAtIndex:i] method] +``` + +Instead, cast the result of `objectAtIndex:` to the appropriate type, and then call the method. (TODO learn about, then decide a policy on, soft-generics on things other than `id`) + +The format of a class should be + +```objective-c +@interface name : parent { + // ivars +} +// properties +- (ret)method; +// more methods +@end + +@implementation name + +- (ret)method +{ + // note the lack of semicolon +} + +@end +``` diff --git a/README.md b/README.md index 01275c0e..dfa5b0b5 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ This README is being written.
## Announcements +* **TODO** + * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](TODO). + * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. + * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. From fcbf706559dfeda6253589cc526ba428a31421fe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 16:58:18 -0400 Subject: [PATCH 266/487] More README fixups. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index dfa5b0b5..ca2c5ac0 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,10 @@ When you run a binary directly from the Terminal, however, you are running it di See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl). +## Contributing + +See `CONTRIBUTING.md`. + ## Screenshots From examples/controlgallery: From 0c7ca9a34691ebe7d3d184f618b5e159f505c9fc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 17:03:29 -0400 Subject: [PATCH 267/487] Quick contributtion fixups. --- CONTRIBUTING.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b8dcda9..1a35c211 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,6 +22,8 @@ Expressions should have a spce around binary operators, and use parentheses wher When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`. +There should be a newline between a function's variables and a function's code. After that, you can place newlines to delimit different parts of a function, but don't go crazy. + In the event you are unsure of something, refer to existing libui code for examples. I may wind up fixing minor details later anyway, so don't fret about getting minor details right the first time. ### Naming @@ -40,7 +42,7 @@ Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case ### Compatibility -libui takes backward compatibility seriously. Your code should not break the current compatibility requirements. All platforms provide a series of macros, defined in the various `uipriv_*.h*` files (or `winapi.hpp` on Windows), that specify the minimum required version. If you find yourself needing to remove these or ignore resultant warnings or errors, you're probably breaking compatibility. +libui takes backward compatibility seriously. Your code should not break the current compatibility requirements. All platforms provide a series of macros, defined in the various `uipriv_*.h` files (or `winapi.hpp` on Windows), that specify the minimum required version. If you find yourself needing to remove these or ignore resultant warnings or errors, you're probably breaking compatibility. Choosing to drop older versions of Windows, GTK+, and OS X that I could have easily continued to support was not done lightly. If you want to discuss dropping support for an older version of any of these for the benefit of libui, file an issue pleading your case (see below). @@ -76,7 +78,8 @@ The format of a class should be ```c++ class name : public ancestor { - // private variables here + int privateVariable; + // etc. public: // public stuff here }; @@ -88,6 +91,8 @@ Avoid GNU-specific language features. I build with strict C99 conformance. ### OS X-specific notes +Avoid GNU-specific/clang-specific language features. I build with strict C99 conformance. + libui is presently **not** ARC-compliant. Features that require ARC should be avoided for now. I may consider changing this in the future, but it will be a significant change. To ensure maximum compiler output in the event of a coding error, there should not be any implicit method calls in Objective-C code. For instance, don't do @@ -105,13 +110,13 @@ The format of a class should be // ivars } // properties -- (ret)method; +- (ret)method:(int)arg; // more methods @end @implementation name -- (ret)method +- (ret)method:(int)arg { // note the lack of semicolon } From 39530a0a2edd06b726716afa04337ab2816e557a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 17:08:10 -0400 Subject: [PATCH 268/487] Another quick CONTRIBUTING fix. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a35c211..d04792f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,7 +71,7 @@ Despite using C++, please refrain from using the following: The following are not recommended, for consistency with the rest of libui: - variable declarations anywhere in a function (keep them all at the top) -- `for (int...` (C++11 foreach syntax is fine, though) +- `for (int x...` (C++11 foreach syntax is fine, though) - omitting the `struct` on type names for ordinary structs The format of a class should be From 8728dcb5c07402883d080d54712346cb01dd2a67 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 23:33:25 -0400 Subject: [PATCH 269/487] Started a new example for showing off OpenType features. This just lays out the UI and draws the string. --- CONTRIBUTING.md | 6 +- README.md | 2 +- examples/CMakeLists.txt | 10 ++- examples/opentype/main.c | 173 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 examples/opentype/main.c diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d04792f2..7147ff4e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ libui uses K&R C formatting rules for overall code structure: spaces after keywo Use hard tabs, NOT spaces, for indentation. I use a proportional-width font and my text editor doesn't set tabs to a multiple of the space width, so I *will* be able to tell. If you use a fixed-width font, I suggest setting a tab width of 4 spaces per tab, but don't put diagrams in comments with hard tabs, because not everyone does this. -Expressions should have a spce around binary operators, and use parentheses where it would help humans gather the meaning of an expression, regardless of whether a computer could tell what is correct. +Expressions should have a space around binary operators, and use parentheses where it would help humans gather the meaning of an expression, regardless of whether a computer could tell what is correct. When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`. @@ -40,6 +40,10 @@ Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case (TODO I am writing an API documentation tool; once that becomes stable enough I can talk about documenting libui properly. You'll see vestiges of it throughout ui.h, though.) +### Other commenting + +(TODO write this part) + ### Compatibility libui takes backward compatibility seriously. Your code should not break the current compatibility requirements. All platforms provide a series of macros, defined in the various `uipriv_*.h` files (or `winapi.hpp` on Windows), that specify the minimum required version. If you find yourself needing to remove these or ignore resultant warnings or errors, you're probably breaking compatibility. diff --git a/README.md b/README.md index ca2c5ac0..443c3bb2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This README is being written.
## Announcements * **TODO** - * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](TODO). + * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](TODO). There are also two new examples for this new api: `drawtext` (which shows the whole API at a glance) and `opentype` (which focuses on OpenType features). * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. * **27 November 2016** diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e011db23..f2289d21 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -42,9 +42,17 @@ _add_example(drawtext target_include_directories(drawtext PRIVATE drawtext) +_add_example(opentype + opentype/main.c + ${_EXAMPLE_RESOURCES_RC} +) +target_include_directories(opentype + PRIVATE opentype) + add_custom_target(examples DEPENDS controlgallery histogram cpp-multithread - drawtext) + drawtext + opentype) diff --git a/examples/opentype/main.c b/examples/opentype/main.c new file mode 100644 index 00000000..00c3f451 --- /dev/null +++ b/examples/opentype/main.c @@ -0,0 +1,173 @@ +// 10 june 2017 +#include +#include +#include +#include +#include "../../ui.h" + +uiWindow *mainwin; +uiFontButton *fontButton; +uiEntry *textEntry; +uiCheckbox *nullFeatures; +uiArea *area; + +uiAttributedString *attrstr = NULL; + +static void remakeAttrStr(void) +{ + char *text; + uiOpenTypeFeatures *otf; + uiAttributeSpec spec; + + if (attrstr != NULL) + uiFreeAttributedString(attrstr); + + text = uiEntryText(textEntry); + attrstr = uiNewAttributedString(text); + uiFreeText(text); + + if (!uiCheckboxChecked(nullFeatures)) { + otf = uiNewOpenTypeFeatures(); + // TODO + spec.Type = uiAttributeFeatures; + spec.Features = otf; + uiAttributedStringSetAttribute(attrstr, &spec, + 0, uiAttributedStringLen(attrstr)); + // and uiAttributedString copied otf + uiFreeOpenTypeFeatures(otf); + } + + uiAreaQueueRedrawAll(area); +} + +// TODO make a variable of main()? in all programs? +static uiAreaHandler handler; + +static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) +{ + uiDrawTextLayout *layout; + uiDrawTextLayoutParams lp; + uiDrawFontDescriptor desc; + + memset(&lp, 0, sizeof (uiDrawTextLayoutParams)); + lp.String = attrstr; + uiFontButtonFont(fontButton, &desc); + lp.DefaultFont = &desc; + lp.Width = p->AreaWidth; + lp.Align = uiDrawTextAlignLeft; + layout = uiDrawNewTextLayout(&lp); + uiDrawText(p->Context, layout, 0, 0); + uiDrawFreeTextLayout(layout); +} + +static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) +{ + // do nothing +} + +static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) +{ + // do nothing +} + +static void handlerDragBroken(uiAreaHandler *ah, uiArea *a) +{ + // do nothing +} + +static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) +{ + // reject all keys + return 0; +} + +static int onClosing(uiWindow *w, void *data) +{ + // TODO change the others to be like this? (the others destroy here rather than later) + // TODO move this below uiQuit()? + uiControlHide(uiControl(w)); + uiQuit(); + return 0; +} + +static int shouldQuit(void *data) +{ + uiControlDestroy(uiControl(mainwin)); + return 1; +} + +int main(void) +{ + uiInitOptions o; + const char *err; + uiGrid *grid; + uiBox *vbox; + + handler.Draw = handlerDraw; + handler.MouseEvent = handlerMouseEvent; + handler.MouseCrossed = handlerMouseCrossed; + handler.DragBroken = handlerDragBroken; + handler.KeyEvent = handlerKeyEvent; + + memset(&o, 0, sizeof (uiInitOptions)); + err = uiInit(&o); + if (err != NULL) { + fprintf(stderr, "error initializing ui: %s\n", err); + uiFreeInitError(err); + return 1; + } + + uiOnShouldQuit(shouldQuit, NULL); + + // TODO 800x600? the size of the GTK+ example? + mainwin = uiNewWindow("libui OpenType Features Example", 640, 480, 1); + uiWindowSetMargined(mainwin, 1); + uiWindowOnClosing(mainwin, onClosing, NULL); + + grid = uiNewGrid(); + uiGridSetPadded(grid, 1); + uiWindowSetChild(mainwin, uiControl(grid)); + + fontButton = uiNewFontButton(); + uiGridAppend(grid, uiControl(fontButton), + 0, 0, 1, 1, + // TODO are these Y values correct? + 0, uiAlignFill, 0, uiAlignCenter); + + textEntry = uiNewEntry(); + uiEntrySetText(textEntry, "afford afire aflight"); + uiGridAppend(grid, uiControl(textEntry), + 1, 0, 1, 1, + // TODO are these Y values correct too? + // TODO add a baseline align? or a form align? + 1, uiAlignFill, 0, uiAlignCenter); + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + uiGridAppend(grid, uiControl(vbox), + 0, 1, 1, 1, + 0, uiAlignFill, 1, uiAlignFill); + + nullFeatures = uiNewCheckbox("NULL uiOpenTypeFeatures"); + uiBoxAppend(vbox, uiControl(nullFeatures), 0); + + // TODO separator (if other stuff isn't a tab) + + // TODO other stuff + + area = uiNewArea(&handler); + uiGridAppend(grid, uiControl(area), + 1, 1, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + + // and set up the initial draw + remakeAttrStr(); + + uiControlShow(uiControl(mainwin)); + uiMain(); + + uiControlDestroy(uiControl(mainwin)); + uiFreeAttributedString(attrstr); + uiUninit(); + return 0; +} From 85c39c6cb32e5b2c0183e8c788836d10d5f5d138 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Jun 2017 00:37:26 -0400 Subject: [PATCH 270/487] Connected events in the opentype example. --- examples/opentype/main.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/examples/opentype/main.c b/examples/opentype/main.c index 00c3f451..5f8c0f70 100644 --- a/examples/opentype/main.c +++ b/examples/opentype/main.c @@ -5,6 +5,8 @@ #include #include "../../ui.h" +// TODO the grid simply flat out does not work on OS X + uiWindow *mainwin; uiFontButton *fontButton; uiEntry *textEntry; @@ -81,6 +83,21 @@ static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) return 0; } +static void onFontChanged(uiFontButton *b, void *data) +{ + remakeAttrStr(); +} + +static void onTextChanged(uiEntry *e, void *data) +{ + remakeAttrStr(); +} + +static void onNULLToggled(uiCheckbox *c, void *data) +{ + remakeAttrStr(); +} + static int onClosing(uiWindow *w, void *data) { // TODO change the others to be like this? (the others destroy here rather than later) @@ -129,6 +146,7 @@ int main(void) uiWindowSetChild(mainwin, uiControl(grid)); fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, onFontChanged, NULL); uiGridAppend(grid, uiControl(fontButton), 0, 0, 1, 1, // TODO are these Y values correct? @@ -136,6 +154,7 @@ int main(void) textEntry = uiNewEntry(); uiEntrySetText(textEntry, "afford afire aflight"); + uiEntryOnChanged(textEntry, onTextChanged, NULL); uiGridAppend(grid, uiControl(textEntry), 1, 0, 1, 1, // TODO are these Y values correct too? @@ -149,6 +168,7 @@ int main(void) 0, uiAlignFill, 1, uiAlignFill); nullFeatures = uiNewCheckbox("NULL uiOpenTypeFeatures"); + uiCheckboxOnToggled(nullFeatures, onNULLToggled, NULL); uiBoxAppend(vbox, uiControl(nullFeatures), 0); // TODO separator (if other stuff isn't a tab) From c8f4ccc712dfde6453ed6494bb83915973cef50b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Jun 2017 12:21:49 -0400 Subject: [PATCH 271/487] Did a patch for OS X. Fixing grid is gonna be FUN. --- examples/opentype/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/opentype/main.c b/examples/opentype/main.c index 5f8c0f70..0872aaa2 100644 --- a/examples/opentype/main.c +++ b/examples/opentype/main.c @@ -173,6 +173,14 @@ int main(void) // TODO separator (if other stuff isn't a tab) + // TODO needed for this to be testable on os x without rewriting everything again + { + int x; + + for (x = 0; x < 10; x++) + uiBoxAppend(vbox, uiControl(uiNewEntry()), 0); + } + // TODO other stuff area = uiNewArea(&handler); From c3568d51624465912b6432c8b1766dd8551e2a76 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 12 Jun 2017 23:42:45 -0400 Subject: [PATCH 272/487] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 41184b93..e15ef19e 100644 --- a/TODO.md +++ b/TODO.md @@ -173,3 +173,5 @@ function: ide_editor_map_bin_add() - Mouse ClickLock: do we need to do anything special? *should* we? https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx - consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553 + +- determine whether MSGF_USER is for and if it's correct for our uiArea message filter (if we have one) From 6b989f80caf4ba1fc10b83d1f232d46361c9a842 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Jun 2017 21:17:44 -0400 Subject: [PATCH 273/487] More TODOs. --- TODO.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TODO.md b/TODO.md index e15ef19e..0a8cf8ee 100644 --- a/TODO.md +++ b/TODO.md @@ -175,3 +175,11 @@ function: ide_editor_map_bin_add() - consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553 - determine whether MSGF_USER is for and if it's correct for our uiArea message filter (if we have one) + +- source file encoding and MSVC compiler itself? https://stackoverflow.com/questions/20518040/how-can-i-get-the-directwrite-padwrite-sample-to-work + - also need to worry about object file and output encoding... + - this also names the author of the padwrite sample + +- OpenType features TODOs + - https://stackoverflow.com/questions/32545675/what-are-the-default-typography-settings-used-by-idwritetextlayout + - feature/shaping interaction rules for arabic: https://www.microsoft.com/typography/OpenTypeDev/arabic/intro.htm From fd7c436b8a4b2c391cd731cf97414ecb93187887 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 14 Jun 2017 22:07:41 -0400 Subject: [PATCH 274/487] More TODOs. --- windows/drawtext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index f173f471..59d64097 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -7,6 +7,7 @@ // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... // - empty string: nLines == 1 and all checks out except extents has x == 0 when not left aligned // - paragraph alignment is subject to RTL mirroring; see if it is on other platforms +// - add overhang info to metrics? // TODO verify our renderer is correct, especially with regards to snapping From cc8a41268779d0ba732b508e2a5f511761a26c04 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Jun 2017 17:15:32 -0400 Subject: [PATCH 275/487] More TODOs. --- windows/dwrite.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 2e9ce928..498ef376 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -66,6 +66,7 @@ WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings * hr = names->FindLocaleName(fc->userLocale, &index, &exists); if (hr != S_OK || (hr == S_OK && !exists)) hr = names->FindLocaleName(L"en-us", &index, &exists); + // TODO check hr again here? or did I decide that would be redundant because COM requires output arguments to be filled regardless of return value? if (!exists) index = 0; From 4610a5a363a55933ce2f425b0a40066072e7621e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Jun 2017 11:19:24 -0400 Subject: [PATCH 276/487] More TODOs. --- TODO.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TODO.md b/TODO.md index 0a8cf8ee..e5be9a4a 100644 --- a/TODO.md +++ b/TODO.md @@ -183,3 +183,10 @@ function: ide_editor_map_bin_add() - OpenType features TODOs - https://stackoverflow.com/questions/32545675/what-are-the-default-typography-settings-used-by-idwritetextlayout - feature/shaping interaction rules for arabic: https://www.microsoft.com/typography/OpenTypeDev/arabic/intro.htm + - other stuff, mostly about UIs and what users expect to be able to set + - https://klim.co.nz/blog/towards-an-ideal-opentype-user-interface/ + - https://libregraphicsmeeting.org/2016/designing-for-many-applications-opentype-features-ui/ + - https://www.youtube.com/watch?v=wEyDhsH076Y + - https://twitter.com/peter_works + - http://ilovetypography.com/2014/10/22/better-ui-for-better-typography-adobe-petition/ + - http://silgraphite.sourceforge.net/ui/studynote.html From e6ee2b0dbd2b94618fea200405b41bb549537f8b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Jun 2017 20:10:42 -0400 Subject: [PATCH 277/487] Some TODO elminiation (for once). One TODO got reshuffled. --- darwin/opentype.m | 1 + ui.h | 3 +++ ui_attrstr.h | 22 ++++++++++++++++------ unix/opentype.c | 1 + windows/opentype.cpp | 3 +-- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 46bdaf19..1a2f3ab1 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -47,6 +47,7 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c NSNumber *tn; tn = mkMapObject(mkTag(a, b, c, d)); + // documented as doing nothing if tn is not in otf->tags [otf->tags removeObjectForKey:tn]; } diff --git a/ui.h b/ui.h index 72e5eaef..1a875cab 100644 --- a/ui.h +++ b/ui.h @@ -2,6 +2,9 @@ // TODO add a uiVerifyControlType() function that can be used by control implementations to verify controls +// TODOs +// - make getters that return whether something exists accept a NULL pointer to discard the value (and thus only return that the thing exists?) + #ifndef __LIBUI_UI_H__ #define __LIBUI_UI_H__ diff --git a/ui_attrstr.h b/ui_attrstr.h index 0024442c..835cee39 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,7 +19,10 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; -// TODO either here or above, say that only one attribute can be applied per attribute type per character +// uiAttribute specifies the types of possible attributes that can be +// applied to a uiAttributedString. For every byte in the +// uiAttributedString, at most one value of each attribute type can +// be applied. // TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is @@ -64,7 +67,7 @@ _UI_ENUM(uiAttribute) { // uiAttributeFeatures changes the OpenType features of the // text it is applied to. Use the Features field of uiAttributeSpec. - uiAttributeFeatures, // use Features + uiAttributeFeatures, }; _UI_ENUM(uiDrawUnderlineStyle) { @@ -124,15 +127,22 @@ _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); // uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. -// TODO what happens if the tag isn't there? +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); // uiOpenTypeFeaturesGet() determines whether the given feature // tag is present in otf. If it is, *value is set to the tag's value and // nonzero is returned. Otherwise, zero is returned. -// TODO zero-fill value unconditionally? and if so, to other functions in libui -// TODO allow NULL for value? and throughout libui? +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. // TODO const-correct this function (can we do that given the members of the struct on some platforms being full blown objects that may or may not themselves be const-correct?) _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); diff --git a/unix/opentype.c b/unix/opentype.c index 79b1d93a..764da50a 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -56,6 +56,7 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + // will just return FALSE if the tag is not in otf->tags (half-documented as such), so we can use it safely g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); } diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 89b5913c..8fc37175 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -39,10 +39,9 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char (*(otf->tags))[mktag(a, b, c, d)] = value; } -// TODO what should happen if a/b/c/d isn't defined? -// TODO what does std::map do if a/b/c/d isn't defined? void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + // this will just return 0 if nothing was removed (if I'm reading the help pages I've found correctly) otf->tags->erase(mktag(a, b, c, d)); } From 3e20e4670c7937a1a2b6814313a8f2a8df87a05a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 14:50:03 -0400 Subject: [PATCH 278/487] Made a decision on const correctness in uiOpenTypeFeatures. --- darwin/opentype.m | 9 +++++---- ui.h | 1 + ui_attrstr.h | 13 +++++++------ unix/opentype.c | 11 ++++++----- windows/opentype.cpp | 5 +++-- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 1a2f3ab1..890aa02a 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -51,7 +51,8 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c [otf->tags removeObjectForKey:tn]; } -int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +// TODO will the const wreck stuff up? +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { NSNumber *tn, *vn; @@ -78,7 +79,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - ret = (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, mapObjectValue(vn), data); // TODO for all: require exact match? if (ret == uiForEachStop) @@ -98,7 +99,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature // TODO explain all this // TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) // TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static uiForEach otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; @@ -133,7 +134,7 @@ static uiForEach otfArrayForEachAAT(char a, char b, char c, char d, uint32_t val } // TODO find out which fonts differ in AAT small caps and test them with this -static uiForEach otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; CFDictionaryRef innerDict; diff --git a/ui.h b/ui.h index 1a875cab..039d45f4 100644 --- a/ui.h +++ b/ui.h @@ -4,6 +4,7 @@ // TODOs // - make getters that return whether something exists accept a NULL pointer to discard the value (and thus only return that the thing exists?) +// - const-correct everything #ifndef __LIBUI_UI_H__ #define __LIBUI_UI_H__ diff --git a/ui_attrstr.h b/ui_attrstr.h index 835cee39..430d65d0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -104,8 +104,10 @@ _UI_ENUM(uiDrawUnderlineColor) { // TODO invalid features typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; -// TODO pass the feature set? (resolve const struct issue below first) -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); // @role uiOpenTypeFeatures constructor // uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures @@ -143,12 +145,11 @@ _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b // for a feature. You should likewise not treat a missing feature as // having a value of zero either. Instead, a missing feature should // be treated as having some unspecified default value. -// TODO const-correct this function (can we do that given the members of the struct on some platforms being full blown objects that may or may not themselves be const-correct?) -_UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); // uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. -// TODO make other enumerators const (and in general const-correct everything) (but see the const struct TODO below and the const struct object member TODO above) +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); // uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. diff --git a/unix/opentype.c b/unix/opentype.c index 764da50a..30933ea8 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -60,9 +60,8 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); } -// TODO should this be before Add and Remove? -// TODO better name than Get? -int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +// TODO will the const wreck stuff up? +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { gboolean found; gpointer gv; @@ -77,6 +76,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char } struct otfForEach { + const uiOpenTypeFeatures *otf; uiOpenTypeFeaturesForEachFunc f; void *data; uiForEach ret; @@ -96,7 +96,7 @@ static void foreach(gpointer key, gpointer value, gpointer data) b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - ofe->ret = (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); + ofe->ret = (*(ofe->f))(ofe->otf, (char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) @@ -104,6 +104,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures struct otfForEach ofe; memset(&ofe, 0, sizeof (struct otfForEach)); + ofe.otf = otf; ofe.f = f; ofe.data = data; g_hash_table_foreach(otf->tags, foreach, &ofe); @@ -183,7 +184,7 @@ out: // see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings // TODO make this a g_hash_table_foreach() function (which requires duplicating code)? -static uiForEach toCSS(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach toCSS(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) { // TODO is there a G_STRING()? GString *s = (GString *) data; diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 8fc37175..235f0388 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -45,7 +45,8 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c otf->tags->erase(mktag(a, b, c, d)); } -int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +// TODO will the const wreck stuff up? +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { tagmap::const_iterator iter; @@ -69,7 +70,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures b = (uint8_t) ((iter->first >> 8) & 0xFF); c = (uint8_t) ((iter->first >> 16) & 0xFF); d = (uint8_t) ((iter->first >> 24) & 0xFF); - ret = (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, iter->second, data); if (ret == uiForEachStop) return; From 5d7128781b163f603a0dc596314755b26152e4fe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 21:45:18 -0400 Subject: [PATCH 279/487] And const-corrected uiAttributedStringForEachAttribute(). --- common/attrstr.c | 2 +- darwin/attrstr.m | 2 +- ui_attrstr.h | 13 ++++++++++--- unix/attrstr.c | 2 +- windows/attrstr.cpp | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index c5466edf..ab406ad8 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -306,7 +306,7 @@ void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec attrlistInsertAttribute(s->attrs, spec, start, end); } -// TODO introduce an iterator? +// LONGTERM introduce an iterator object instead? void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { attrlistForEach(s->attrs, s, f, data); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 6975ca06..89c09d22 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -203,7 +203,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; diff --git a/ui_attrstr.h b/ui_attrstr.h index 430d65d0..58ec6199 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -106,7 +106,8 @@ typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // uiOpenTypeFeaturesForEachFunc is the type of the function // invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. +// feature in otf. Refer to that function's documentation for more +// details. typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); // @role uiOpenTypeFeatures constructor @@ -162,6 +163,7 @@ typedef struct uiAttributeSpec uiAttributeSpec; // TODO note that pointers are copied // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit // (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) +// TODO normalize documentation between typedefs and structs struct uiAttributeSpec { uiAttribute Type; const char *Family; @@ -174,8 +176,11 @@ struct uiAttributeSpec { const uiOpenTypeFeatures *Features; }; -// TODO how would we make spec const in this case, to prevent fields from being modified? -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -201,6 +206,8 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); +// TODO document this +// TODO possibly copy the spec each time to ensure it doesn't get clobbered _UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/unix/attrstr.c b/unix/attrstr.c index 245ae7f6..36141b7c 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -65,7 +65,7 @@ static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttr pango_attr_list_insert(p->attrs, attr); } -static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index f180f74b..ca805237 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -49,7 +49,7 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; From 180b6429ef2c297a64509699b80d0a6d0153c28e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 22:00:46 -0400 Subject: [PATCH 280/487] More documentation, const correctness, and TODO wrangling. --- common/attrstr.c | 4 ++-- ui.h | 1 + ui_attrstr.h | 28 ++++++++++++++++++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index ab406ad8..e34b7d27 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -73,12 +73,12 @@ void uiFreeAttributedString(uiAttributedString *s) uiFree(s); } -const char *uiAttributedStringString(uiAttributedString *s) +const char *uiAttributedStringString(const uiAttributedString *s) { return s->s; } -size_t uiAttributedStringLen(uiAttributedString *s) +size_t uiAttributedStringLen(const uiAttributedString *s) { return s->len; } diff --git a/ui.h b/ui.h index 039d45f4..0f7dce64 100644 --- a/ui.h +++ b/ui.h @@ -5,6 +5,7 @@ // TODOs // - make getters that return whether something exists accept a NULL pointer to discard the value (and thus only return that the thing exists?) // - const-correct everything +// - normalize documentation between typedefs and structs #ifndef __LIBUI_UI_H__ #define __LIBUI_UI_H__ diff --git a/ui_attrstr.h b/ui_attrstr.h index 58ec6199..82a532ba 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -163,7 +163,6 @@ typedef struct uiAttributeSpec uiAttributeSpec; // TODO note that pointers are copied // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit // (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) -// TODO normalize documentation between typedefs and structs struct uiAttributeSpec { uiAttribute Type; const char *Family; @@ -194,21 +193,42 @@ _UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); // uiAttributedStringString() returns the textual content of s as a // '\0'-terminated UTF-8 string. The returned pointer is valid until // the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(uiAttributedString *s); +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); // uiAttributedStringLength() returns the number of UTF-8 bytes in // the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(uiAttributedString *s); +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. _UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with its text. _UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtAttributed functions + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the range [start, end). _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); + // TODO document this // TODO possibly copy the spec each time to ensure it doesn't get clobbered -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; From 271a3bc0222e1526487de524e381f10de454344d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 23:34:59 -0400 Subject: [PATCH 281/487] More TODOs. --- ui_attrstr.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 82a532ba..27fd7473 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -210,7 +210,8 @@ _UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, cons // along with its text. _UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); -// TODO add the Append and InsertAtAttributed functions +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length // uiAttributedStringDelete() deletes the characters and attributes of // s in the range [start, end). From 2bea99116d3192f2fad1f401380d4a7e563ffd73 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Jun 2017 19:54:35 -0400 Subject: [PATCH 282/487] More TODOs. --- TODO.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO.md b/TODO.md index e5be9a4a..4b1c4452 100644 --- a/TODO.md +++ b/TODO.md @@ -190,3 +190,6 @@ function: ide_editor_map_bin_add() - https://twitter.com/peter_works - http://ilovetypography.com/2014/10/22/better-ui-for-better-typography-adobe-petition/ - http://silgraphite.sourceforge.net/ui/studynote.html + +- add NXCOMPAT (DEP awareness) to the Windows builds + - and ASLR too? or is that not a linker setting From 40bc1505cef3a23bdf2d6aee0a8fe8c0456dd8ea Mon Sep 17 00:00:00 2001 From: Ashley Date: Mon, 24 Jul 2017 02:21:18 +0200 Subject: [PATCH 283/487] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01275c0e..b8b407e3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # libui: a portable GUI library for C This README is being written.
-[![Build Status](https://travis-ci.org/andlabs/libui.svg)](https://travis-ci.org/andlabs/libui) +[![Build Status](https://travis-ci.org/andlabs/libui.svg?branch=master)](https://travis-ci.org/andlabs/libui) ## Announcements From b98888a628dcecc64c31e18a29aa8a31c6ae2dff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 20 Aug 2017 05:30:36 -0400 Subject: [PATCH 284/487] More TODOs. --- windows/utilwin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 11f9a415..28950674 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -8,6 +8,7 @@ // - It handles WM_QUERYENDSESSION requests. // - It handles WM_WININICHANGE and forwards the message to any child windows that request it. // - It handles executing functions queued to run by uiQueueMain(). +// TODO explain why it isn't message-only #define utilWindowClass L"libui_utilWindowClass" From 2702f4f8748dd0a905c529a1d0f1b3e1b8a71246 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Sep 2017 16:14:58 -0400 Subject: [PATCH 285/487] More TODOs. --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index 4b1c4452..7d148d7f 100644 --- a/TODO.md +++ b/TODO.md @@ -193,3 +193,8 @@ function: ide_editor_map_bin_add() - add NXCOMPAT (DEP awareness) to the Windows builds - and ASLR too? or is that not a linker setting + +OS X: embedding an Info.plist into a binary directly +https://www.objc.io/issues/6-build-tools/mach-o-executables/ +TODO will this let Dictation work? +TODO investigate ad-hoc codesigning From 21c99859e48dff785f3fe0202dfd73e5c4f4924b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Sep 2017 15:33:46 -0400 Subject: [PATCH 286/487] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 7d148d7f..adf8e9de 100644 --- a/TODO.md +++ b/TODO.md @@ -198,3 +198,5 @@ OS X: embedding an Info.plist into a binary directly https://www.objc.io/issues/6-build-tools/mach-o-executables/ TODO will this let Dictation work? TODO investigate ad-hoc codesigning + +https://blogs.msdn.microsoft.com/oldnewthing/20040112-00/?p=41083 def files for decoration (I forget if I said this earlier) From 7b89fb21a13e2b08834742a68cea4e41fff42b33 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Sep 2017 21:03:41 -0400 Subject: [PATCH 287/487] More TODOs. --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index adf8e9de..aa9c75b3 100644 --- a/TODO.md +++ b/TODO.md @@ -200,3 +200,8 @@ TODO will this let Dictation work? TODO investigate ad-hoc codesigning https://blogs.msdn.microsoft.com/oldnewthing/20040112-00/?p=41083 def files for decoration (I forget if I said this earlier) + +TODO ClipCursor() stuff; probably not useful for libui but still +https://blogs.msdn.microsoft.com/oldnewthing/20140102-00/?p=2183 +https://blogs.msdn.microsoft.com/oldnewthing/20061117-03/?p=28973 +https://msdn.microsoft.com/en-us/library/windows/desktop/ms648383(v=vs.85).aspx From c8c51502cb27ba0c9b939d5f09850d4476bd48c7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 19 Sep 2017 22:22:04 -0400 Subject: [PATCH 288/487] More TODOs. --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index aa9c75b3..6653a6cf 100644 --- a/TODO.md +++ b/TODO.md @@ -205,3 +205,8 @@ TODO ClipCursor() stuff; probably not useful for libui but still https://blogs.msdn.microsoft.com/oldnewthing/20140102-00/?p=2183 https://blogs.msdn.microsoft.com/oldnewthing/20061117-03/?p=28973 https://msdn.microsoft.com/en-us/library/windows/desktop/ms648383(v=vs.85).aspx + +https://cmake.org/Wiki/CMake_Useful_Variables +set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") +On Unix systems, this will make linker report any unresolved symbols from object files (which is quite typical when you compile many targets in CMake projects, but do not bother with linking target dependencies in proper order). +(I used to have something like this back when I used makefiles; did it convert in? I forget) From cb5f9b3c9c79bc2dcd7782b158b566439f3a520c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 26 Sep 2017 18:54:23 -0400 Subject: [PATCH 289/487] More TODOs. --- TODO.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TODO.md b/TODO.md index 6653a6cf..d831395b 100644 --- a/TODO.md +++ b/TODO.md @@ -210,3 +210,12 @@ https://cmake.org/Wiki/CMake_Useful_Variables set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") On Unix systems, this will make linker report any unresolved symbols from object files (which is quite typical when you compile many targets in CMake projects, but do not bother with linking target dependencies in proper order). (I used to have something like this back when I used makefiles; did it convert in? I forget) + +look into these for the os x port +https://developer.apple.com/documentation/appkit/view_management/nseditor?language=objc +https://developer.apple.com/documentation/appkit/view_management/nseditorregistration?language=objc + +for future versions of the os x port +https://developer.apple.com/documentation/appkit/nslayoutguide?language=objc and anchors +https://developer.apple.com/documentation/appkit/nsuserinterfacecompression?language=objc https://developer.apple.com/documentation/appkit/nsuserinterfacecompressionoptions?language=objc +though at some point we'll be able to use NSStackView and NSGridView directly, so... From e597d8ce3ba96eab2836f5d34b787049d9373a75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 27 Sep 2017 14:24:07 -0400 Subject: [PATCH 290/487] More TODOs. --- darwin/drawtext.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index fd5a5bbb..43c8b4e9 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -190,6 +190,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO how is this affected by a non-identity CTM? CGContextTranslateCTM(c->c, 0, c->height); CGContextScaleCTM(c->c, 1.0, -1.0); + // TODO save the text matrix CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); // wait, that's not enough; we need to offset y values to account for our new flipping From 55a97093f54a7bb0e102b0feb83dd2e5cce0cd17 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 29 Sep 2017 09:58:43 -0400 Subject: [PATCH 291/487] Removed a stale TODO. --- darwin/attrstr.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 89c09d22..cdf7334f 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,8 +1,6 @@ // 12 february 2017 #import "uipriv_darwin.h" -// LONGTERM FUTURE for typographic features, on 10.10 we can use OpenType tags directly! - // this is what AppKit does internally // WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm static NSColor *spellingColor = nil; From 98e06716b6b3ff029ac112b2759a15c8476d4021 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Oct 2017 14:42:15 -0400 Subject: [PATCH 292/487] More TODOs. --- TODO.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/TODO.md b/TODO.md index d831395b..dee9d997 100644 --- a/TODO.md +++ b/TODO.md @@ -219,3 +219,31 @@ for future versions of the os x port https://developer.apple.com/documentation/appkit/nslayoutguide?language=objc and anchors https://developer.apple.com/documentation/appkit/nsuserinterfacecompression?language=objc https://developer.apple.com/documentation/appkit/nsuserinterfacecompressionoptions?language=objc though at some point we'll be able to use NSStackView and NSGridView directly, so... + +Cocoa PDFs +https://developer.apple.com/documentation/appkit/nspdfimagerep?language=objc +https://developer.apple.com/documentation/coregraphics?language=objc +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_pagination/osxp_pagination.html#//apple_ref/doc/uid/20001051-119037 +https://developer.apple.com/documentation/appkit/nsprintoperation/1529269-pdfoperationwithview?language=objc +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printapps/osxp_printapps.html#//apple_ref/doc/uid/20000861-BAJBFGED +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printingapi/osxp_printingapi.html#//apple_ref/doc/uid/10000083i-CH2-SW2 +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printinfo/osxp_printinfo.html#//apple_ref/doc/uid/20000864-BAJBFGED +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printlayoutpanel/osxp_printlayoutpanel.html#//apple_ref/doc/uid/20000863-BAJBFGED +https://developer.apple.com/documentation/appkit/nspagelayout?language=objc +https://developer.apple.com/documentation/appkit/nsprintinfo?language=objc +https://developer.apple.com/documentation/applicationservices/core_printing?language=objc +https://developer.apple.com/documentation/applicationservices/1463247-pmcreatesession?language=objc +https://developer.apple.com/documentation/applicationservices/pmprintsession?language=objc +https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1463416-pmsessionbeginpagenodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1506831-anonymous/kpmdestinationprocesspdf?language=objc +https://developer.apple.com/documentation/applicationservices/1461960-pmcreategenericprinter?language=objc +https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1464527-pmsessionenddocumentnodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1461952-pmsessiongetcggraphicscontext?language=objc +https://developer.apple.com/library/content/technotes/tn2248/_index.html +https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_aboutprinting/osxp_aboutprt.html + +- run os x code with `OBJC_DEBUG_MISSING_POOLS=YES` and other `OBJC_HELP=YES` options + - turn off the autorelease pool to make sure we're not autoreleasing improperly From eab8a98aa9eb0928657bf47e98fe51785124ff6d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Oct 2017 17:05:16 -0400 Subject: [PATCH 293/487] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index dee9d997..cfb31fe3 100644 --- a/TODO.md +++ b/TODO.md @@ -247,3 +247,5 @@ https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Print - run os x code with `OBJC_DEBUG_MISSING_POOLS=YES` and other `OBJC_HELP=YES` options - turn off the autorelease pool to make sure we're not autoreleasing improperly + +TODO investigate -Weverything in clang alongside -Wall in MSVC (and in gcc too maybe...) From 4d57a12dbc9678d8183ba309f3d8df8d6bb796be Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 9 Oct 2017 20:48:35 -0400 Subject: [PATCH 294/487] More general TODOs... I'm starting to feel like I'm over my head with text stuff, and stopping for those few months has made me forget what I wanted to do :| or maybe forget the specifics, I'm not sure what. All I know is that I accumulated more TODOs specific to this branch than I thought and now I'm just :S --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index cfb31fe3..7a783af9 100644 --- a/TODO.md +++ b/TODO.md @@ -249,3 +249,8 @@ https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Print - turn off the autorelease pool to make sure we're not autoreleasing improperly TODO investigate -Weverything in clang alongside -Wall in MSVC (and in gcc too maybe...) + +mac os x accessibility +- https://developer.apple.com/documentation/appkit/nsworkspace/1524656-accessibilitydisplayshoulddiffer?language=objc +- https://developer.apple.com/documentation/appkit/nsworkspace/1526290-accessibilitydisplayshouldincrea?language=objc +- https://developer.apple.com/documentation/appkit/nsworkspace/1533006-accessibilitydisplayshouldreduce?language=objc From 9f1e1b25be2f417dbb50074a9005d15b0e506b38 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Oct 2017 23:40:07 -0400 Subject: [PATCH 295/487] Started converting the code in Core Text itself that determines what weight a font has into pseudocode. --- doc/export/ctweights | 218 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 doc/export/ctweights diff --git a/doc/export/ctweights b/doc/export/ctweights new file mode 100644 index 00000000..31508150 --- /dev/null +++ b/doc/export/ctweights @@ -0,0 +1,218 @@ +// pseudo-go + +func (f *CTFont) IsRegistered() bool { + n := f.Attribute(kCTFontRegistrationScopeAttribute) + if n == nil { + return false + } + return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone +} + +// based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&) +func (f *Font) WeightFromFontRegistry32() float32 { + var weight float32 + + cgfont := f.CGFont() +} + +// because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done +func (f *Font) WeightFromFontRegistry() float64 { + return CFNumberWithFloat32(f.WeightFromFontRegistry32()).Float64Value() +} + +// based on CoreText dylib's __Z13WeightOfClasst — WeightOfClass(unsigned short) +func CoreText_WeightOfClass(usWeightClass uint16) float64 { + if usWeightClass >= 11 { + // do nothing; we are preserving the original asm comparisons + } else { + usWeightClass *= 100 + } + + // figure out what two floats our weight will be between + i := usWeightClass / 100 + j := i + 1 + if j > 10 { + j = 10 + } + b := float64(i * 100) + c := float64(j * 100) + + a := float64(0) + if b != c { + a = float64(usWeightClass) + a -= b + c -= b + a /= c + } + scales := []float32{ + float32as(-1.000000, 0xbf800000), + float32as(-0.700000, 0xbf333333), + float32as(-0.500000, 0xbf000000), + float32as(-0.230000, 0xbe6b851f), + float32as(0.000000, 0x0), + float32as(0.200000, 0x3e4ccccd), + float32as(0.300000, 0x3e99999a), + float32as(0.400000, 0x3ecccccd), + float32as(0.600000, 0x3f19999a), + float32as(0.800000, 0x3f4ccccd), + float32as(1.000000, 0x3f800000), + } + c = float64(scale[i]) + b = float64[scale[j]) + return fma(a, b, c) +} + +// based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*) +func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) { + str.Fold(kCFCompareCaseInsensitive, nil) + + var weightNameMap = []struct { + key string + val float32 + }{ + { "ultra light", float32as(-0.800000, 0xbf4ccccd) }, + { "ultra black", float32as(0.750000, 0x3f400000) }, + { "extra light", float32as(-0.500000, 0xbf000000) }, + { "ultralight", float32as(-0.800000, 0xbf4ccccd) }, + { "ultrablack", float32as(0.750000, 0x3f400000) }, + { "extrablack", float32as(0.800000, 0x3f4ccccd) }, + { "extralight", float32as(-0.500000, 0xbf000000) } + { "heavy face", float32as(0.560000, 0x3f0f5c29) }, + { "semi light", float32as(-0.200000, 0xbe4ccccd) }, + { "extra bold", float32as(0.500000, 0x3f000000) }, + { "ultra bold", float32as(0.700000, 0x3f333333) }, + { "heavyface", float32as(0.560000, 0x3f0f5c29) }, + { "extrabold", float32as(0.500000, 0x3f000000) }, + { "ultrabold", float32as(0.700000, 0x3f333333) }, + { "semilight", float32as(-0.200000, 0xbe4ccccd) }, + { "demi bold", float32as(0.250000, 0x3e800000) }, + { "semi bold", float32as(0.300000, 0x3e99999a) }, + { "demibold", float32as(0.250000, 0x3e800000) }, + { "semibold", float32as(0.300000, 0x3e99999a) }, + { "hairline", float32as(-0.700000, 0xbf333333) }, + { "medium", float32as(0.230000, 0x3e6b851f) }, + { "poster", float32as(0.800000, 0x3f4ccccd) }, + { "light", float32as(-0.400000, 0xbecccccd) }, + { "heavy", float32as(0.560000, 0x3f0f5c29) }, + { "extra", float32as(0.500000, 0x3f000000) }, + { "black", float32as(0.620000, 0x3f1eb852) }, + { "super", float32as(0.620000, 0x3f1eb852) }, + { "obese", float32as(0.850000, 0x3f59999a) }, + { "lite", float32as(-0.400000, 0xbecccccd) }, + { "book", float32as(-0.230000, 0xbe6b851f) }, + { "demi", float32as(0.250000, 0x3e800000) }, + { "semi", float32as(0.300000, 0x3e99999a) }, + { "thin", float32as(-0.500000, 0xbf000000) }, + { "bold", float32as(0.400000, 0x3ecccccd) }, + { "nord", float32as(0.800000, 0x3f4ccccd) }, + { "fat", float32as(0.750000, 0x3f400000) }, + { "w1", float32as(-0.700000, 0xbf333333) }, + { "w2", float32as(-0.500000, 0xbf000000) }, + { "w3", float32as(-0.230000, 0xbe6b851f) }, + { "w4", float32as(0.000000, 0x0) }, + { "w5", float32as(0.230000, 0x3e6b851f) }, + { "w6", float32as(0.300000, 0x3e99999a) }, + { "w7", float32as(0.440000, 0x3ee147ae) }, + { "w8", float32as(0.540000, 0x3f0a3d71) }, + { "w9", float32as(0.620000, 0x3f1eb852) }, + } + for _, m := range weightNameMap { + if strstr(str, m.key) != nil { + val := CFNumberWithFloat32(m.val) + return val.Float64Value(), true + } + } + return 0, false +} + +// based on CoreText dylib's __ZNK9TBaseFont29CreateTraitsValuesPerFontInfoEP12MetadataFlag — TBaseFont::CreateTraitsValuesPerFontInfo(MetadataFlag*) const +func (f *CTFont) Weight() float64 { + if f.IsRegistered() { + return f.WeightFromFontRegistry() + } + + weight := float64as(2.0, 0x4000000000000000) + ebx := -1 + hasWeight := false + + name := f.Name(kCTFontPostScriptNameKey) + if name != nil { + switch *name { + case "LucidaGrande": + weight = float64as(0.000000, 0x0) + hasWeight = true + case ".LucidaGrandeUI": + weight = float64as(0.000000, 0x0) + hasWeight = true + case "STHeiti": + weight = float64as(0.240000, 0x3fceb851eb851eb8) + hasWeight = true + case "STXihei": + weight = float64as(-0.100000, 0xbfb999999999999a) + hasWeight = true + case "TimesNewRomanPSMT": + weight = float64as(0.000000, 0x0) + hasWeight = true + // there is one more hardcoded case, for "Times-Roman", but that will only set the class style, not the weight + } + } + + os2table := f.Table('OS/2') + if os2table != nil { + if !hasWeight { + var usWeightClass uint16 + + valid := false + if os2table.Len() > 77 { + b := os2table.Bytes() + usWeightClass = uint16be(b[4:6]) + if usWeightClass > 1000 { + weight = 0 + hasWeight = false + } else { + valid = true + } + } else { + usWeightClass = 0 + valid = true + } + if valid { + weight = CoreText_WeightOfClass(usWeightClass) + hasWeight = true + } + } + } + + styleGlossaryNames := []string{ + kCTFontSubFamilyNameKey, + kCTFontFullNameKey, + kCTFontFamilyNameKey, + } + for _, key := range styleGlossaryNames { + name := f.Name(key) + if name == nil { + continue + } + candidate, ok := CoreText_WeightByStyleGlossaryString(*name) + if !ok { + continue + } + if !hasWeight { + weight = candidate + hasWeight = true + } + } + + if hasWeight { + return weight + } + return 0 +} + +func (f *Font) ShouldEnableBoldSymbolicTrait() bool { + if f.IsRegistered() { + return f.ShouldEnableBoldSymbolicTraitFromRegistry() + } + no := f.Weight() <= float64as(0.239000, 0x3fce978d4fdf3b64) + return !no +} From 683bd47491a4444267d5d7d0ddad5c907ab85139 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Oct 2017 23:59:54 -0400 Subject: [PATCH 296/487] More ctweights stuff. --- doc/export/ctweights | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/doc/export/ctweights b/doc/export/ctweights index 31508150..4cf2839d 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -8,11 +8,79 @@ func (f *CTFont) IsRegistered() bool { return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone } +// based on CoreGraphics dylib's _CGFontCopyName +// note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) +// also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such +const ( + kCGFontNameKeyPostScriptName = xxx +) +func (f *CGFont) CopyName(key int) (string, bool) { +} + // based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&) func (f *Font) WeightFromFontRegistry32() float32 { var weight float32 + var hasWeight bool = false cgfont := f.CGFont() + if f.RegistryHasMetadata() { + wv := f.RegistryMetadataValueForKey("MTD_Typeface_Weight_VisualDescriptor") + if wv != nil { + if wn, ok := wv.(string); ok { + // note: uses CFStringCompare(0) + switch wn { + case "reg": + weight = float32as(0.000000, 0x0) + hasWeight = true + case "semi": + weight = float32as(0.300000, 0x3e99999a) + hasWeight = true + case "bold": + weight = float32as(0.400000, 0x3ecccccd) + hasWeight = true + case "light": + weight = float32as(-0.400000, 0xbecccccd) + hasWeight = true + case "med": + weight = float32as(0.230000, 0x3e6b851f) + hasWeight = true + case "heavy": + weight = float32as(0.560000, 0x3f0f5c29) + hasWeight = true + case "black": + weight = float32as(0.620000, 0x3f1eb852) + hasWeight = true + case "thin": + weight = float32as(-0.600000, 0xbf19999a) + hasWeight = true + case "ulight": + weight = float32as(-0.800000, 0xbf4ccccd) + hasWeight = true + } + } + } + } + + cgpsname, ok := cgfont.CopyName(kCGFontNameKeyPostScriptName) + if ok { + // note: uses CFStringCompare(0) + switch cgpsname { + case "LucidaGrande", + ".LucidaGrandeUI", + ".Keyboard": + weight = float32as(0.000000, 0x0) + hasWeight = true + case "STHeiti": + weight = float32as(0.240000, 0x3e75c28f) + hasWeight = true + case "STXihei": + weight = float32as(-0.100000, 0xbdcccccd) + hasWeight = true + case "TimesNewRomanPSMT": + weight = float32as(0.000000, 0x0) + hasWeight = true + } + } } // because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done From f94de2eef850086bba72667e23890675fb7cca75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 19 Oct 2017 19:15:46 -0400 Subject: [PATCH 297/487] And finally finished the core Core Text weight determination functionality pseudo-Go-code. God damn. And I have a funny feeling stretches are going to be a tad bit more inconsistent too... :| Not quite complete, though; need to fill in the name table parsing rules first. --- doc/export/ctweights | 263 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 2 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index 4cf2839d..4d25cc5f 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -13,8 +13,125 @@ func (f *CTFont) IsRegistered() bool { // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such const ( kCGFontNameKeyPostScriptName = xxx + kCGFontNameKeyPreferredSubfamily = xxx + kCGFontNameKeyFontSubfamily = xxx + kCGFontNameKeyFullName = xxx + kCGFontNameKeyPreferredFamily = xxx + kCGFontNameKeyFontFamily = xxx ) func (f *CGFont) CopyName(key int) (string, bool) { + // TODO +} + +// based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const +func RegistryDetermineOS2Weight(table *CFData) (float32, bool) { + if table == nil { + return 0, false + } + if table.Len() < 78 { + return 0, false + } + + b := table.Bytes() + usWeightClass := uint16be(b[4:6]) + if usWeightClass >= 10 { + // do nothing; we are preserving the original asm comparisons + } else { + usWeightClass *= 100 + } + /* TODO: +000000000000b37e mov dx, word [rax+4] +000000000000b382 mov cx, dx +000000000000b385 rol cx, 0x8 +000000000000b389 movzx esi, cx +000000000000b38c imul ecx, ecx, 100 +000000000000b38f cmp esi, 10 +000000000000b392 cmovae cx, si +000000000000b396 test dx, dx +000000000000b399 cmove cx, si + what's the function of the last two instructions? */ + + // note that this is an unsigned comparison, so underflow will result in a number > 998 + // the effect is the same as (usWeightClass == 0) || (usWeightClass >= 1000) + if (usWeightClass - 1) > 998 { + // note the - 2 here; the switch cases below reflect that! + // also note that b[0x22] and panose will be unsigned, so underflow will result in a number > 9 + panose := b[0x22] - 2 + if panose > 9 { + return 0, false + } + switch panose { + case 0: + return float32as(-0.500000, 0xbf000000), true + case 1: + return float32as(-0.400000, 0xbecccccd), true + case 2: + // yes, this returns false; I don't know why + return float32as(-0.300000, 0xbe99999a), false + case 3: + return float32as(-0.230000, 0xbe6b851f), true + case 4: + return float32as(0.230000, 0x3e6b851f), true + case 5: + return float32as(0.250000, 0x3e800000), true + case 6: + return float32as(0.400000, 0x3ecccccd), true + case 7: + return float32as(0.560000, 0x3f0f5c29), true + case 8: + return float32as(0.620000, 0x3f1eb852), true + case 9: + return float32as(0.800000, 0x3f4ccccd), true + } + // should not reach here + } + + // let's mimic the assembly here too + // the gotos avoid the massive if nesting + // also note I'm using Go idioms and not saying "else return", imagine those if you must + if usWeightClass > 100 { + if usWeightClass > 200 { + goto do201AndUp + } + return float32as(-0.500000, 0xbf000000), true + } + return float32as(-0.800000, 0xbf4ccccd), true + +do201AndUp: + if usWeightClass > 300 { + if usWeightClass > 400 { + goto do401AndUp + } + return float32as(0.000000, 0x0), true + } + return float32as(-0.400000, 0xbecccccd), true + +do401AndUp: + if usWeightClass > 500 { + if usWeightClass > 600 { + goto do601AndUp + } + return float32as(0.250000, 0x3e800000), true + } + return float32as(0.230000, 0x3e6b851), true + +do601AndUp: + if usWeightClass > 700 { + if usWeightClass > 800 { + goto do801AndUp + } + return float32as(0.500000, 0x3f000000), true + } + return float32as(0.400000, 0x3ecccccd), true + +do801AndUp: + if usWeightClass > 900 { + if usWeightClass > 950 { + return float32(0.800000, 0x3f4ccccd), true + } + return float32(0.750000, 0x3f400000), true + } + return float32as(0.620000, 0x3f1eb852), true } // based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&) @@ -81,6 +198,148 @@ func (f *Font) WeightFromFontRegistry32() float32 { hasWeight = true } } + + styleGlossaryStrings := []int{ + kCGFontNameKeyPreferredSubfamily, + kCGFontNameKeyFontSubfamily, + kCGFontNameKeyFullName, + kCGFontNameKeyPreferredFamily, + kCGFontNameKeyFontFamily, + } + weightNameMap := []struct { + key string + val float32 + }{ + { "Ultra Light", float32as(-0.800000f, 0xbf4ccccd) }, + { "Ultra Black", float32as(0.750000f, 0x3f400000) }, + { "Extra Light", float32as(-0.500000f, 0xbf000000) }, + { "UltraBlack", float32as(0.750000f, 0x3f400000) }, + { "ExtraBlack", float32as(0.800000f, 0x3f4ccccd) }, + { "UltraLight", float32as(-0.800000f, 0xbf4ccccd) }, + { "ExtraLight", float32as(-0.500000f, 0xbf000000) }, + { "Ultra Thin", float32as(-0.800000f, 0xbf4ccccd) }, + { "Extra Thin", float32as(-0.800000f, 0xbf4ccccd) }, + { "Heavy Face", float32as(0.560000f, 0x3f0f5c29) }, + { "Semi Light", float32as(-0.200000f, 0xbe4ccccd) }, + { "Extra Bold", float32as(0.500000f, 0x3f000000) }, + { "Ultra Bold", float32as(0.700000f, 0x3f333333) }, + { "HeavyFace", float32as(0.560000f, 0x3f0f5c29) }, + { "ExtraBold", float32as(0.500000f, 0x3f000000) }, + { "UltraBold", float32as(0.700000f, 0x3f333333) }, + { "Ext Black", float32as(0.800000f, 0x3f4ccccd) }, + { "SemiLight", float32as(-0.200000f, 0xbe4ccccd) }, + { "Demi Bold", float32as(0.250000f, 0x3e800000) }, + { "Semi Bold", float32as(0.300000f, 0x3e99999a) }, + { "Ext Light", float32as(-0.500000f, 0xbf000000) }, + { "Ext Bold", float32as(0.500000f, 0x3f000000) }, + { "DemiBold", float32as(0.250000f, 0x3e800000) }, + { "SemiBold", float32as(0.300000f, 0x3e99999a) }, + { "HairLine", float32as(-0.800000f, 0xbf4ccccd) }, + { "Ext Thin", float32as(-0.800000f, 0xbf4ccccd) }, + { "Medium", float32as(0.230000f, 0x3e6b851f) }, + { "Poster", float32as(0.800000f, 0x3f4ccccd) }, + { "Light", float32as(-0.400000f, 0xbecccccd) }, + { "Ultra", float32as(0.500000f, 0x3f000000) }, + { "Heavy", float32as(0.560000f, 0x3f0f5c29) }, + { "Extra", float32as(0.500000f, 0x3f000000) }, + { "Black", float32as(0.620000f, 0x3f1eb852) }, + { "Super", float32as(0.620000f, 0x3f1eb852) }, + { "Obese", float32as(0.850000f, 0x3f59999a) }, + { "Lite", float32as(-0.400000f, 0xbecccccd) }, + { "Book", float32as(-0.230000f, 0xbe6b851f) }, + { "Demi", float32as(0.250000f, 0x3e800000) }, + { "Semi", float32as(0.300000f, 0x3e99999a) }, + { "Thin", float32as(-0.500000f, 0xbf000000) }, + { "Bold", float32as(0.400000f, 0x3ecccccd) }, + { "Nord", float32as(0.800000f, 0x3f4ccccd) }, + { "Fat", float32as(0.750000f, 0x3f400000) }, + { "W1", float32as(-0.230000f, 0xbe6b851f) }, + { "W2", float32as(-0.500000f, 0xbf000000) }, + { "W3", float32as(-0.230000f, 0xbe6b851f) }, + { "W4", float32as(0.000000f, 0x0) }, + { "W5", float32as(0.230000f, 0x3e6b851f) }, + { "W6", float32as(0.300000f, 0x3e99999a) }, + { "W7", float32as(0.440000f, 0x3ee147ae) }, + { "W8", float32as(0.540000f, 0x3f0a3d71) }, + { "W9", float32as(0.620000f, 0x3f1eb852) }, + } + for _, key := range styleGlossaryStrings { + if hasWeight { + break + } + str, ok := cgfont.CopyName(key) + if !ok { + continue + } + for _, m := range weightNameMap { + if str.FindWithOptions(m.key, CFRangeMake(0, str.CFStringLength()), kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral, nil) { + weight = m.val + hasWeight = true + break + } + } + } + + if !hasWeight { + os2table := cgfont.TableForTag('OS/2') + weight, hasWeight = RegistryDetermineOS2Weight(os2table) + } + + if !hasWeight { + headtable := cgfont.TableForTag('head') + if headtable != nil { + if headtable.Len() >= 54 { + b := headtable.Bytes() + if (b[0x2d] & 1) != 0 { + weight = float32as(0.400000, 0x3ecccccd) + hasWeight = true + } + } + } + } + + styleGlossaryAbbreviationKeys := []int{ + kCGFontNameKeyPreferredSubfamily, + kCGFontNameKeyFontSubfamily, + } + abbreviatedWeightNameMap := []struct { + key string + val float32 + }{ + { "EL", float32as(-0.200000, 0xbe4ccccd) }, + { "EB", float32as(0.500000, 0x3f000000) }, + { "SB", float32as(0.300000, 0x3e99999a) }, + { "UH", float32as(0.800000, 0x3f4ccccd) }, + { "U", float32as(0.700000, 0x3f333333) }, + { "L", float32as(-0.400000, 0xbecccccd) }, + { "H", float32as(0.560000, 0x3f0f5c29) }, + { "B", float32as(0.400000, 0x3ecccccd) }, + { "M", float32as(0.230000, 0x3e6b851f) }, + { "R", float32as(0.000000, 0x0) }, + } + if !hasWeight { + for _, key := range styleGlossaryAbbreviationStrings { + str, ok := cgfont.CopyName(key) + if !ok { + continue + } + for _, m := range abbreviatedWeightNameMap { + if str.Compare(m.key, kCFCompareCaseInsensitive) == kCFCompareEqualTo { + weight = m.val + hasWeight = true + break + } + } + if hasWeight { + break + } + } + } + + if !hasWeight { + return float32as(0.000000, 0x0) + } + return weight } // because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done @@ -92,6 +351,7 @@ func (f *Font) WeightFromFontRegistry() float64 { func CoreText_WeightOfClass(usWeightClass uint16) float64 { if usWeightClass >= 11 { // do nothing; we are preserving the original asm comparisons + // and yes, this one is 11, but the one above is 10 } else { usWeightClass *= 100 } @@ -133,8 +393,7 @@ func CoreText_WeightOfClass(usWeightClass uint16) float64 { // based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*) func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) { str.Fold(kCFCompareCaseInsensitive, nil) - - var weightNameMap = []struct { + weightNameMap := []struct { key string val float32 }{ From 9731d2e836ce866c339bfa7e94c951eabe6ebf68 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 19 Oct 2017 23:54:46 -0400 Subject: [PATCH 298/487] More work on ctweights. --- doc/export/ctweights | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index 4d25cc5f..d89c0e9b 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -12,15 +12,43 @@ func (f *CTFont) IsRegistered() bool { // note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such const ( - kCGFontNameKeyPostScriptName = xxx - kCGFontNameKeyPreferredSubfamily = xxx - kCGFontNameKeyFontSubfamily = xxx - kCGFontNameKeyFullName = xxx - kCGFontNameKeyPreferredFamily = xxx - kCGFontNameKeyFontFamily = xxx + kCGFontNameKeyPostScriptName = 0x6 + kCGFontNameKeyPreferredSubfamily = 0x11 + kCGFontNameKeyFontSubfamily = 0x2 + kCGFontNameKeyFullName = 0x4 + kCGFontNameKeyPreferredFamily = 0x10 + kCGFontNameKeyFontFamily = 0x1 ) func (f *CGFont) CopyName(key int) (string, bool) { - // TODO + var nameCount int + var stringOffset int + + table := f.TableForTag('name') + b := table.Bytes() + n := table.Len() + + // the asm does some obtuse logic to get to these conditions, so I will simplify the logic here + if n >= 4 { + nameCount = int(uint16be(b[2:4])) + } else { + nameCount = 0 + } + if n >= 6 { + stringOffset = int(uint16be(b[4:6])) + } else { + stringOffset = 0 + } + + type NameRecord struct { + PlatformID uint16 + PlatformSpecificID uint16 + LanguageID uint16 + NameID uint16 + Length uint16 + Offset uint16 + } + + pos := x } // based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const From 9b28bd5ecd0bd1f9251d05b3a31f7c0adde69765 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Oct 2017 14:14:32 -0400 Subject: [PATCH 299/487] More name table work. --- doc/export/ctweights | 152 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 139 insertions(+), 13 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index d89c0e9b..7d50567b 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -8,6 +8,42 @@ func (f *CTFont) IsRegistered() bool { return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone } +xx this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there +type x_list struct { + Data interface{} + Next *x_list +} + +func (x *x_list) Prepend(data interface{}) *x_list { + y := malloc(sizeof (x_list)) + if y != nil { + y.data = data + y.next = x + return y + } + return x +} + +func (x *x_list) Reverse() *x_list { + if x == nil { + return nil + } + + var old, next *x_list + + next = nil + for { + old = x + x = old.next + old.next = next + next = old + if x == nil { + break + } + } + return old +} + // based on CoreGraphics dylib's _CGFontCopyName // note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such @@ -20,23 +56,27 @@ const ( kCGFontNameKeyFontFamily = 0x1 ) func (f *CGFont) CopyName(key int) (string, bool) { - var nameCount int - var stringOffset int - table := f.TableForTag('name') b := table.Bytes() n := table.Len() - // the asm does some obtuse logic to get to these conditions, so I will simplify the logic here - if n >= 4 { - nameCount = int(uint16be(b[2:4])) - } else { - nameCount = 0 + xx this code looks weird, but we're imitating the assembly, or the effective effects thereof + offCount := uint16(0) + offStringOffset := uint16(2) + if n > 1 { + offCount = 2 + offStringOffset = 4 } - if n >= 6 { - stringOffset = int(uint16be(b[4:6])) - } else { - stringOffset = 0 + + count := uint16(0) + if int(offCount) <= n { + count = uint16be(b[offCount:offCount + 2]) + } + + offNameRecord := offStringOffset + 2 + stringOffset := uint16(0) + if int(offNameRecord) <= n { + stringOffset = uint16be(b[offStringOffset:offStringOffset + 2]) } type NameRecord struct { @@ -48,7 +88,93 @@ func (f *CGFont) CopyName(key int) (string, bool) { Offset uint16 } - pos := x + var nameList *x_list + + addrStrings := offNameRecords + (12 * count) + if addrStrings != stringOffset { + goto hasLanguageTags + } + pos := offNameRecords + if count == 0 { + xx TODO note assembly logic here + } else { + for { + var nr NameRecord + + nr.PlatformID = 0 + next := pos + 2 + if int(next) <= n { + nr.PlatformID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.PlatformSpecificID = 0 + next = pos + 2 + if int(next) <= n { + nr.PlatformSpecificID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.LanguageID = 0 + next = pos + 2 + if int(next) <= n { + nr.LanguageID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.NameID = 0 + next = pos + 2 + if int(next) <= n { + nr.NameID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.Length = 0 + next = pos + 2 + if int(next) <= n { + nr.Length = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.Offset = 0 + next = pos + 2 + if int(next) <= n { + nr.Offset = uint16be(b[pos:pos + 2]) + pos = next + } + + strpos := stringOffset + nr.Offset + if strpos >= n { + xx TODO put comment about imitating the assembly comparisons here + } else { + realLen := nr.Length + strend = strpos + nr.Length + if strend > n { + realLen = nr.Length - strpos + strend = strpos + realLen + } + b := malloc(12 + realLen + 1) + if b != nil { + name := (*sfnt_name_t)(b) + name.PlatformID = nr.PlatformID + name.PlatformSpecificID = nr.PlatformSpecificID + name.LanguageID = nr.LanguageID + name.NameID = nr.NameID + name.Length = realLen + memcpy(&(name.Name), b[strpos:strend], realLen) + name.Name[realLen] = 0 + nameList = nameList.Prepend(name) + } + } + count-- + if count == 0 { + break + } + } + } + nameList = nameList.Reverse() + +hasLanguageTags: } // based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const From 2858c56528933df84fdc0548c8bffcc324e55c11 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 02:54:39 -0400 Subject: [PATCH 300/487] CGFontCopyName() is too intricate to recreate. We might have to wind up calling it directly... --- doc/export/ctweights | 59 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index 7d50567b..f9feb90b 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -8,7 +8,7 @@ func (f *CTFont) IsRegistered() bool { return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone } -xx this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there +// this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there type x_list struct { Data interface{} Next *x_list @@ -44,6 +44,23 @@ func (x *x_list) Reverse() *x_list { return old } +func (x *x_list) Concat(y *x_list) *x_list { + if x == nil { + return y + } + start := x + z := x + for { + x = z + z = z.next + if z == nil { + break + } + } + x.next = y + return start +} + // based on CoreGraphics dylib's _CGFontCopyName // note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such @@ -60,7 +77,7 @@ func (f *CGFont) CopyName(key int) (string, bool) { b := table.Bytes() n := table.Len() - xx this code looks weird, but we're imitating the assembly, or the effective effects thereof + // this code looks weird, but we're imitating the assembly, or the effective effects thereof offCount := uint16(0) offStringOffset := uint16(2) if n > 1 { @@ -96,7 +113,7 @@ func (f *CGFont) CopyName(key int) (string, bool) { } pos := offNameRecords if count == 0 { - xx TODO note assembly logic here + // TODO note assembly logic here } else { for { var nr NameRecord @@ -145,7 +162,7 @@ func (f *CGFont) CopyName(key int) (string, bool) { strpos := stringOffset + nr.Offset if strpos >= n { - xx TODO put comment about imitating the assembly comparisons here + // TODO put comment about imitating the assembly comparisons here } else { realLen := nr.Length strend = strpos + nr.Length @@ -175,6 +192,40 @@ func (f *CGFont) CopyName(key int) (string, bool) { nameList = nameList.Reverse() hasLanguageTags: + add_localized_names := func(platformID uint16, platformSpecificID uint16, to *x_list) *x_list { + out := (*x_list)(nil) + if nameList == nil { + xx TODO logic verbatim etc. + } else { + x := nameList + for { + name := (*sfnt_name_t)(x.data) + if name.PlatformID != platformID { + xx TODO + } else { + if platformSpecificID == 0xFFFF || name.PlatformSpecificID == platformSpecificID { + out = out.Prepend(name) + } + } + x = x.next + if x == nil { + break + } + } + } + out = out.Reverse() + return to.Concat(out) + } + localized := (*x_list)(nil) + localized = add_localized_names(0x1, 0xFFFF, localized) + localized = add_localized_names(0, 0xFFFF, localized) + localized = add_localized_names(0x3, 0xFFFF, localized) + localized = add_localized_names(0x1, 0, localized) + localized = add_localized_names(0x3, 0x9, localized) + localized = add_localized_names(0x3, 0x409, localized) + + sysLocale := CFLocaleGetSystem() + } // based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const From 3b316ddb5bdf932487277c9da8f1a958a0b868dc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:07:07 -0400 Subject: [PATCH 301/487] Sorted all the possible Core Text weight values into lists. Now to process those lists. --- doc/export/ctweightvalues | 247 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 doc/export/ctweightvalues diff --git a/doc/export/ctweightvalues b/doc/export/ctweightvalues new file mode 100644 index 00000000..ee7f17dc --- /dev/null +++ b/doc/export/ctweightvalues @@ -0,0 +1,247 @@ +registered font, preexisting metadata weight +"reg": float32as(0.000000, 0x0) +"semi": float32as(0.300000, 0x3e99999a) +"bold": float32as(0.400000, 0x3ecccccd) +"light": float32as(-0.400000, 0xbecccccd) +"med": float32as(0.230000, 0x3e6b851f) +"heavy": float32as(0.560000, 0x3f0f5c29) +"black": float32as(0.620000, 0x3f1eb852) +"thin": float32as(-0.600000, 0xbf19999a) +"ulight": float32as(-0.800000, 0xbf4ccccd) + +registered font, postscript name (probably only for TrueType and OpenType) special cases, overrides the above +"LucidaGrande": float32as(0.000000, 0x0) +".LucidaGrandeUI": float32as(0.000000, 0x0) +".Keyboard": float32as(0.000000, 0x0) +"STHeiti": float32as(0.240000, 0x3e75c28f) +"STXihei": float32as(-0.100000, 0xbdcccccd) +"TimesNewRomanPSMT": float32as(0.000000, 0x0) + +registered font, style glossary strings, tried in this order (possibly TrueType and OpenType only): preferred subfamily, subfamily, full name, preferred family, family; case-insensitive search, reverse search, "nonliteral" (kCFCompareNonliteral) +"Ultra Light": float32as(-0.800000, 0xbf4ccccd) +"Ultra Black": float32as(0.750000, 0x3f400000) +"Extra Light": float32as(-0.500000, 0xbf000000) +"UltraBlack": float32as(0.750000, 0x3f400000) +"ExtraBlack": float32as(0.800000, 0x3f4ccccd) +"UltraLight": float32as(-0.800000, 0xbf4ccccd) +"ExtraLight": float32as(-0.500000, 0xbf000000) +"Ultra Thin": float32as(-0.800000, 0xbf4ccccd) +"Extra Thin": float32as(-0.800000, 0xbf4ccccd) +"Heavy Face": float32as(0.560000, 0x3f0f5c29) +"Semi Light": float32as(-0.200000, 0xbe4ccccd) +"Extra Bold": float32as(0.500000, 0x3f000000) +"Ultra Bold": float32as(0.700000, 0x3f333333) +"HeavyFace": float32as(0.560000, 0x3f0f5c29) +"ExtraBold": float32as(0.500000, 0x3f000000) +"UltraBold": float32as(0.700000, 0x3f333333) +"Ext Black": float32as(0.800000, 0x3f4ccccd) +"SemiLight": float32as(-0.200000, 0xbe4ccccd) +"Demi Bold": float32as(0.250000, 0x3e800000) +"Semi Bold": float32as(0.300000, 0x3e99999a) +"Ext Light": float32as(-0.500000, 0xbf000000) +"Ext Bold": float32as(0.500000, 0x3f000000) +"DemiBold": float32as(0.250000, 0x3e800000) +"SemiBold": float32as(0.300000, 0x3e99999a) +"HairLine": float32as(-0.800000, 0xbf4ccccd) +"Ext Thin": float32as(-0.800000, 0xbf4ccccd) +"Medium": float32as(0.230000, 0x3e6b851f) +"Poster": float32as(0.800000, 0x3f4ccccd) +"Light": float32as(-0.400000, 0xbecccccd) +"Ultra": float32as(0.500000, 0x3f000000) +"Heavy": float32as(0.560000, 0x3f0f5c29) +"Extra": float32as(0.500000, 0x3f000000) +"Black": float32as(0.620000, 0x3f1eb852) +"Super": float32as(0.620000, 0x3f1eb852) +"Obese": float32as(0.850000, 0x3f59999a) +"Lite": float32as(-0.400000, 0xbecccccd) +"Book": float32as(-0.230000, 0xbe6b851f) +"Demi": float32as(0.250000, 0x3e800000) +"Semi": float32as(0.300000, 0x3e99999a) +"Thin": float32as(-0.500000, 0xbf000000) +"Bold": float32as(0.400000, 0x3ecccccd) +"Nord": float32as(0.800000, 0x3f4ccccd) +"Fat": float32as(0.750000, 0x3f400000) +"W1": float32as(-0.230000, 0xbe6b851f) +"W2": float32as(-0.500000, 0xbf000000) +"W3": float32as(-0.230000, 0xbe6b851f) +"W4": float32as(0.000000, 0x0) +"W5": float32as(0.230000, 0x3e6b851f) +"W6": float32as(0.300000, 0x3e99999a) +"W7": float32as(0.440000, 0x3ee147ae) +"W8": float32as(0.540000, 0x3f0a3d71) +"W9": float32as(0.620000, 0x3f1eb852) + +registered font, OS2 weights; table length >= 78 +1, 10 - 100: float32as(-0.800000, 0xbf4ccccd) +2, 101 - 200: float32as(-0.500000, 0xbf000000) +3, 201 - 300: float32as(-0.400000, 0xbecccccd) +4, 301 - 400: float32as(0.000000, 0x0) +5, 401 - 500: float32as(0.230000, 0x3e6b851) +6, 501 - 600: float32as(0.250000, 0x3e800000) +7, 601 - 700: float32as(0.400000, 0x3ecccccd) +8, 701 - 800: float32as(0.500000, 0x3f000000) +9, 801 - 900: float32as(0.620000, 0x3f1eb852) +901 - 950: float32as(0.750000, 0x3f400000) +951 - 999: float32as(0.800000, 0x3f4ccccd) +0, 1000: panose + 2: float32as(-0.500000, 0xbf000000) + 3: float32as(-0.400000, 0xbecccccd) + 4: !!!! should be float32as(-0.300000, 0xbe99999a) but is treated as invalid instead due to returning false instead of true + 5: float32as(-0.230000, 0xbe6b851f) + 6: float32as(0.230000, 0x3e6b851f) + 7: float32as(0.250000, 0x3e800000) + 8: float32as(0.400000, 0x3ecccccd) + 9: float32as(0.560000, 0x3f0f5c29) + 10: float32as(0.620000, 0x3f1eb852) + 11: float32as(0.800000, 0x3f4ccccd) + +registered font, head table, low bit of byte 0x2D +1: float32as(0.400000, 0x3ecccccd) + +registered font, abbreviated weight glossary, checks for (possibly in TrueType and OpenType only) in order: preferred subfamily, family; case-insensitive strict comparison +"EL": float32as(-0.200000, 0xbe4ccccd) +"EB": float32as(0.500000, 0x3f000000) +"SB": float32as(0.300000, 0x3e99999a) +"UH": float32as(0.800000, 0x3f4ccccd) +"U": float32as(0.700000, 0x3f333333) +"L": float32as(-0.400000, 0xbecccccd) +"H": float32as(0.560000, 0x3f0f5c29) +"B": float32as(0.400000, 0x3ecccccd) +"M": float32as(0.230000, 0x3e6b851f) +"R": float32as(0.000000, 0x0) + +registered font +default: float32as(0.000000, 0x0) + +// based on CoreText dylib's __Z13WeightOfClasst — WeightOfClass(unsigned short) +func CoreText_WeightOfClass(usWeightClass uint16) float64 { + if usWeightClass >= 11 { + // do nothing; we are preserving the original asm comparisons + // and yes, this one is 11, but the one above is 10 + } else { + usWeightClass *= 100 + } + + // figure out what two floats our weight will be between + i := usWeightClass / 100 + j := i + 1 + if j > 10 { + j = 10 + } + b := float64(i * 100) + c := float64(j * 100) + + a := float64(0) + if b != c { + a = float64(usWeightClass) + a -= b + c -= b + a /= c + } + scales := []float32{ + float32as(-1.000000, 0xbf800000), + float32as(-0.700000, 0xbf333333), + float32as(-0.500000, 0xbf000000), + float32as(-0.230000, 0xbe6b851f), + float32as(0.000000, 0x0), + float32as(0.200000, 0x3e4ccccd), + float32as(0.300000, 0x3e99999a), + float32as(0.400000, 0x3ecccccd), + float32as(0.600000, 0x3f19999a), + float32as(0.800000, 0x3f4ccccd), + float32as(1.000000, 0x3f800000), + } + c = float64(scale[i]) + b = float64[scale[j]) + return fma(a, b, c) +} + +unregistered font: kCTFontPostScriptNameKey defaults +"LucidaGrande": float64as(0.000000, 0x0) +".LucidaGrandeUI": float64as(0.000000, 0x0) +"STHeiti": float64as(0.240000, 0x3fceb851eb851eb8) +"STXihei": float64as(-0.100000, 0xbfb999999999999a) +"TimesNewRomanPSMT": float64as(0.000000, 0x0) + + + os2table := f.Table('OS/2') + if os2table != nil { + if !hasWeight { + var usWeightClass uint16 + + valid := false + if os2table.Len() > 77 { + b := os2table.Bytes() + usWeightClass = uint16be(b[4:6]) + if usWeightClass > 1000 { + weight = 0 + hasWeight = false + } else { + valid = true + } + } else { + usWeightClass = 0 + valid = true + } + if valid { + weight = CoreText_WeightOfClass(usWeightClass) + hasWeight = true + } + } + } + + +unregistered font, style glossary, checks against kCTFontSubFamilyNameKey, kCTFontFullNameKey, kCTFontFamilyNameKey; case-insensitive (folded by Unicode rules) strstr() +"ultra light": float32as(-0.800000, 0xbf4ccccd) +"ultra black": float32as(0.750000, 0x3f400000) +"extra light": float32as(-0.500000, 0xbf000000) +"ultralight": float32as(-0.800000, 0xbf4ccccd) +"ultrablack": float32as(0.750000, 0x3f400000) +"extrablack": float32as(0.800000, 0x3f4ccccd) +"extralight": float32as(-0.500000, 0xbf000000) +"heavy face": float32as(0.560000, 0x3f0f5c29) +"semi light": float32as(-0.200000, 0xbe4ccccd) +"extra bold": float32as(0.500000, 0x3f000000) +"ultra bold": float32as(0.700000, 0x3f333333) +"heavyface": float32as(0.560000, 0x3f0f5c29) +"extrabold": float32as(0.500000, 0x3f000000) +"ultrabold": float32as(0.700000, 0x3f333333) +"semilight": float32as(-0.200000, 0xbe4ccccd) +"demi bold": float32as(0.250000, 0x3e800000) +"semi bold": float32as(0.300000, 0x3e99999a) +"demibold": float32as(0.250000, 0x3e800000) +"semibold": float32as(0.300000, 0x3e99999a) +"hairline": float32as(-0.700000, 0xbf333333) +"medium": float32as(0.230000, 0x3e6b851f) +"poster": float32as(0.800000, 0x3f4ccccd) +"light": float32as(-0.400000, 0xbecccccd) +"heavy": float32as(0.560000, 0x3f0f5c29) +"extra": float32as(0.500000, 0x3f000000) +"black": float32as(0.620000, 0x3f1eb852) +"super": float32as(0.620000, 0x3f1eb852) +"obese": float32as(0.850000, 0x3f59999a) +"lite": float32as(-0.400000, 0xbecccccd) +"book": float32as(-0.230000, 0xbe6b851f) +"demi": float32as(0.250000, 0x3e800000) +"semi": float32as(0.300000, 0x3e99999a) +"thin": float32as(-0.500000, 0xbf000000) +"bold": float32as(0.400000, 0x3ecccccd) +"nord": float32as(0.800000, 0x3f4ccccd) +"fat": float32as(0.750000, 0x3f400000) +"w1": float32as(-0.700000, 0xbf333333) +"w2": float32as(-0.500000, 0xbf000000) +"w3": float32as(-0.230000, 0xbe6b851f) +"w4": float32as(0.000000, 0x0) +"w5": float32as(0.230000, 0x3e6b851f) +"w6": float32as(0.300000, 0x3e99999a) +"w7": float32as(0.440000, 0x3ee147ae) +"w8": float32as(0.540000, 0x3f0a3d71) +"w9": float32as(0.620000, 0x3f1eb852) + +func (f *Font) ShouldEnableBoldSymbolicTrait() bool { + if f.IsRegistered() { + return f.ShouldEnableBoldSymbolicTraitFromRegistry() + } + no := f.Weight() <= float64as(0.239000, 0x3fce978d4fdf3b64) + return !no +} From e3deebaa1d01b3bdda5576cbc0ac062216d866e8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:22:50 -0400 Subject: [PATCH 302/487] And sorted out the weights. Now to determine what is and isn't sane. --- doc/export/ctweightsraw | 150 ++++++++++++++++++++++++++++++++++++++ doc/export/weightlist1.sh | 8 ++ doc/export/weightlist2.sh | 10 +++ 3 files changed, 168 insertions(+) create mode 100644 doc/export/ctweightsraw create mode 100644 doc/export/weightlist1.sh create mode 100644 doc/export/weightlist2.sh diff --git a/doc/export/ctweightsraw b/doc/export/ctweightsraw new file mode 100644 index 00000000..d34cf0e0 --- /dev/null +++ b/doc/export/ctweightsraw @@ -0,0 +1,150 @@ +0.000000 0x0 "reg" +0.300000 0x3e99999a "semi" +0.400000 0x3ecccccd "bold" +-0.400000 0xbecccccd "light" +0.230000 0x3e6b851f "med" +0.560000 0x3f0f5c29 "heavy" +0.620000 0x3f1eb852 "black" +-0.600000 0xbf19999a "thin" +-0.800000 0xbf4ccccd "ulight" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 ".Keyboard" +0.240000 0x3e75c28f "STHeiti" +-0.100000 0xbdcccccd "STXihei" +0.000000 0x0 "TimesNewRomanPSMT" +-0.800000 0xbf4ccccd "Ultra Light" +0.750000 0x3f400000 "Ultra Black" +-0.500000 0xbf000000 "Extra Light" +0.750000 0x3f400000 "UltraBlack" +0.800000 0x3f4ccccd "ExtraBlack" +-0.800000 0xbf4ccccd "UltraLight" +-0.500000 0xbf000000 "ExtraLight" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "Extra Thin" +0.560000 0x3f0f5c29 "Heavy Face" +-0.200000 0xbe4ccccd "Semi Light" +0.500000 0x3f000000 "Extra Bold" +0.700000 0x3f333333 "Ultra Bold" +0.560000 0x3f0f5c29 "HeavyFace" +0.500000 0x3f000000 "ExtraBold" +0.700000 0x3f333333 "UltraBold" +0.800000 0x3f4ccccd "Ext Black" +-0.200000 0xbe4ccccd "SemiLight" +0.250000 0x3e800000 "Demi Bold" +0.300000 0x3e99999a "Semi Bold" +-0.500000 0xbf000000 "Ext Light" +0.500000 0x3f000000 "Ext Bold" +0.250000 0x3e800000 "DemiBold" +0.300000 0x3e99999a "SemiBold" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ext Thin" +0.230000 0x3e6b851f "Medium" +0.800000 0x3f4ccccd "Poster" +-0.400000 0xbecccccd "Light" +0.500000 0x3f000000 "Ultra" +0.560000 0x3f0f5c29 "Heavy" +0.500000 0x3f000000 "Extra" +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.850000 0x3f59999a "Obese" +-0.400000 0xbecccccd "Lite" +-0.230000 0xbe6b851f "Book" +0.250000 0x3e800000 "Demi" +0.300000 0x3e99999a "Semi" +-0.500000 0xbf000000 "Thin" +0.400000 0x3ecccccd "Bold" +0.800000 0x3f4ccccd "Nord" +0.750000 0x3f400000 "Fat" +-0.230000 0xbe6b851f "W1" +-0.500000 0xbf000000 "W2" +-0.230000 0xbe6b851f "W3" +0.000000 0x0 "W4" +0.230000 0x3e6b851f "W5" +0.300000 0x3e99999a "W6" +0.440000 0x3ee147ae "W7" +0.540000 0x3f0a3d71 "W8" +0.620000 0x3f1eb852 "W9" +-0.800000 0xbf4ccccd 1, 10 - 100 +-0.500000 0xbf000000 2, 101 - 200 +-0.400000 0xbecccccd 3, 201 - 300 +0.000000 0x0 4, 301 - 400 +0.230000 0x3e6b851 5, 401 - 500 +0.250000 0x3e800000 6, 501 - 600 +0.400000 0x3ecccccd 7, 601 - 700 +0.500000 0x3f000000 8, 701 - 800 +0.620000 0x3f1eb852 9, 801 - 900 +0.750000 0x3f400000 901 - 950 +0.800000 0x3f4ccccd 951 - 999 +-0.500000 0xbf000000 2 +-0.400000 0xbecccccd 3 +of true 4 +-0.230000 0xbe6b851f 5 +0.230000 0x3e6b851f 6 +0.250000 0x3e800000 7 +0.400000 0x3ecccccd 8 +0.560000 0x3f0f5c29 9 +0.620000 0x3f1eb852 10 +0.800000 0x3f4ccccd 11 +0.400000 0x3ecccccd 1 +-0.200000 0xbe4ccccd "EL" +0.500000 0x3f000000 "EB" +0.300000 0x3e99999a "SB" +0.800000 0x3f4ccccd "UH" +0.700000 0x3f333333 "U" +-0.400000 0xbecccccd "L" +0.560000 0x3f0f5c29 "H" +0.400000 0x3ecccccd "B" +0.230000 0x3e6b851f "M" +0.000000 0x0 "R" +0.000000 0x0 default +0.000000 0x0 "LucidaGrande" +0.000000 0x0 ".LucidaGrandeUI" +0.240000 0x3fceb851eb851eb8 "STHeiti" +-0.100000 0xbfb999999999999a "STXihei" +0.000000 0x0 "TimesNewRomanPSMT" +-0.800000 0xbf4ccccd "ultra light" +0.750000 0x3f400000 "ultra black" +-0.500000 0xbf000000 "extra light" +-0.800000 0xbf4ccccd "ultralight" +0.750000 0x3f400000 "ultrablack" +0.800000 0x3f4ccccd "extrablack" +-0.500000 0xbf000000 "extralight" +0.560000 0x3f0f5c29 "heavy face" +-0.200000 0xbe4ccccd "semi light" +0.500000 0x3f000000 "extra bold" +0.700000 0x3f333333 "ultra bold" +0.560000 0x3f0f5c29 "heavyface" +0.500000 0x3f000000 "extrabold" +0.700000 0x3f333333 "ultrabold" +-0.200000 0xbe4ccccd "semilight" +0.250000 0x3e800000 "demi bold" +0.300000 0x3e99999a "semi bold" +0.250000 0x3e800000 "demibold" +0.300000 0x3e99999a "semibold" +-0.700000 0xbf333333 "hairline" +0.230000 0x3e6b851f "medium" +0.800000 0x3f4ccccd "poster" +-0.400000 0xbecccccd "light" +0.560000 0x3f0f5c29 "heavy" +0.500000 0x3f000000 "extra" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.850000 0x3f59999a "obese" +-0.400000 0xbecccccd "lite" +-0.230000 0xbe6b851f "book" +0.250000 0x3e800000 "demi" +0.300000 0x3e99999a "semi" +-0.500000 0xbf000000 "thin" +0.400000 0x3ecccccd "bold" +0.800000 0x3f4ccccd "nord" +0.750000 0x3f400000 "fat" +-0.700000 0xbf333333 "w1" +-0.500000 0xbf000000 "w2" +-0.230000 0xbe6b851f "w3" +0.000000 0x0 "w4" +0.230000 0x3e6b851f "w5" +0.300000 0x3e99999a "w6" +0.440000 0x3ee147ae "w7" +0.540000 0x3f0a3d71 "w8" +0.620000 0x3f1eb852 "w9" diff --git a/doc/export/weightlist1.sh b/doc/export/weightlist1.sh new file mode 100644 index 00000000..0a1267ea --- /dev/null +++ b/doc/export/weightlist1.sh @@ -0,0 +1,8 @@ +# 21 october 2017 +gawk ' +BEGIN { FS = "\t+" } +!/float..as/ { next } +{ i = 0; if ($1 == "") i++ } +(NF-i) != 2 { next } +{ print } +' "$@" diff --git a/doc/export/weightlist2.sh b/doc/export/weightlist2.sh new file mode 100644 index 00000000..df2c546d --- /dev/null +++ b/doc/export/weightlist2.sh @@ -0,0 +1,10 @@ +# 21 october 2017 +gawk ' +{ + gsub(/float..as\(/, "") + gsub(/,/, "", $(NF - 1)) + gsub(/\)$/, "") + split($0, parts, /:/) + print $(NF - 1) "\t" $NF "\t" parts[1] +} +' "$@" From e5e0dca360531d44dce8aea34bb987b55aa0ae15 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:33:38 -0400 Subject: [PATCH 303/487] And processed the weights list. --- doc/export/ctweightsprocessed | 149 ++++++++++++++++++++++++++++++++++ doc/export/ctweightsraw | 21 +++-- doc/export/ctweightvalues | 22 ++--- doc/export/weightlist3.sh | 3 + 4 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 doc/export/ctweightsprocessed create mode 100644 doc/export/weightlist3.sh diff --git a/doc/export/ctweightsprocessed b/doc/export/ctweightsprocessed new file mode 100644 index 00000000..d3ec018f --- /dev/null +++ b/doc/export/ctweightsprocessed @@ -0,0 +1,149 @@ +-0.100000 0xbdcccccd "STXihei" +-0.100000 0xbfb999999999999a "STXihei" +-0.200000 0xbe4ccccd "EL" +-0.200000 0xbe4ccccd "Semi Light" +-0.200000 0xbe4ccccd "SemiLight" +-0.200000 0xbe4ccccd "semi light" +-0.200000 0xbe4ccccd "semilight" +-0.230000 0xbe6b851f "Book" +-0.230000 0xbe6b851f "W1" +-0.230000 0xbe6b851f "W3" +-0.230000 0xbe6b851f "book" +-0.230000 0xbe6b851f "w3" +-0.230000 0xbe6b851f panose 5 +-0.400000 0xbecccccd "L" +-0.400000 0xbecccccd "Light" +-0.400000 0xbecccccd "Lite" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "lite" +-0.400000 0xbecccccd 3, 201 - 300 +-0.400000 0xbecccccd panose 3 +-0.500000 0xbf000000 "Ext Light" +-0.500000 0xbf000000 "Extra Light" +-0.500000 0xbf000000 "ExtraLight" +-0.500000 0xbf000000 "Thin" +-0.500000 0xbf000000 "W2" +-0.500000 0xbf000000 "extra light" +-0.500000 0xbf000000 "extralight" +-0.500000 0xbf000000 "thin" +-0.500000 0xbf000000 "w2" +-0.500000 0xbf000000 2, 101 - 200 +-0.500000 0xbf000000 panose 2 +-0.600000 0xbf19999a "thin" +-0.700000 0xbf333333 "hairline" +-0.700000 0xbf333333 "w1" +-0.800000 0xbf4ccccd "Ext Thin" +-0.800000 0xbf4ccccd "Extra Thin" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ultra Light" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "UltraLight" +-0.800000 0xbf4ccccd "ulight" +-0.800000 0xbf4ccccd "ultra light" +-0.800000 0xbf4ccccd "ultralight" +-0.800000 0xbf4ccccd 1, 10 - 100 +0.000000 0x0 ".Keyboard" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "R" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "W4" +0.000000 0x0 "reg" +0.000000 0x0 "w4" +0.000000 0x0 4, 301 - 400 +0.000000 0x0 default +0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f "M" +0.230000 0x3e6b851f "Medium" +0.230000 0x3e6b851f "W5" +0.230000 0x3e6b851f "med" +0.230000 0x3e6b851f "medium" +0.230000 0x3e6b851f "w5" +0.230000 0x3e6b851f panose 6 +0.240000 0x3e75c28f "STHeiti" +0.240000 0x3fceb851eb851eb8 "STHeiti" +0.250000 0x3e800000 "Demi Bold" +0.250000 0x3e800000 "Demi" +0.250000 0x3e800000 "DemiBold" +0.250000 0x3e800000 "demi bold" +0.250000 0x3e800000 "demi" +0.250000 0x3e800000 "demibold" +0.250000 0x3e800000 6, 501 - 600 +0.250000 0x3e800000 panose 7 +0.300000 0x3e99999a "SB" +0.300000 0x3e99999a "Semi Bold" +0.300000 0x3e99999a "Semi" +0.300000 0x3e99999a "SemiBold" +0.300000 0x3e99999a "W6" +0.300000 0x3e99999a "semi bold" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semibold" +0.300000 0x3e99999a "w6" +0.400000 0x3ecccccd "B" +0.400000 0x3ecccccd "Bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd 'head'[0x2D] & 1 +0.400000 0x3ecccccd 7, 601 - 700 +0.400000 0x3ecccccd panose 8 +0.440000 0x3ee147ae "W7" +0.440000 0x3ee147ae "w7" +0.500000 0x3f000000 "EB" +0.500000 0x3f000000 "Ext Bold" +0.500000 0x3f000000 "Extra Bold" +0.500000 0x3f000000 "Extra" +0.500000 0x3f000000 "ExtraBold" +0.500000 0x3f000000 "Ultra" +0.500000 0x3f000000 "extra bold" +0.500000 0x3f000000 "extra" +0.500000 0x3f000000 "extrabold" +0.500000 0x3f000000 8, 701 - 800 +0.540000 0x3f0a3d71 "W8" +0.540000 0x3f0a3d71 "w8" +0.560000 0x3f0f5c29 "H" +0.560000 0x3f0f5c29 "Heavy Face" +0.560000 0x3f0f5c29 "Heavy" +0.560000 0x3f0f5c29 "HeavyFace" +0.560000 0x3f0f5c29 "heavy face" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavyface" +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.620000 0x3f1eb852 "W9" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.620000 0x3f1eb852 "w9" +0.620000 0x3f1eb852 9, 801 - 900 +0.620000 0x3f1eb852 panose 10 +0.700000 0x3f333333 "U" +0.700000 0x3f333333 "Ultra Bold" +0.700000 0x3f333333 "UltraBold" +0.700000 0x3f333333 "ultra bold" +0.700000 0x3f333333 "ultrabold" +0.750000 0x3f400000 "Fat" +0.750000 0x3f400000 "Ultra Black" +0.750000 0x3f400000 "UltraBlack" +0.750000 0x3f400000 "fat" +0.750000 0x3f400000 "ultra black" +0.750000 0x3f400000 "ultrablack" +0.750000 0x3f400000 901 - 950 +0.800000 0x3f4ccccd "Ext Black" +0.800000 0x3f4ccccd "ExtraBlack" +0.800000 0x3f4ccccd "Nord" +0.800000 0x3f4ccccd "Poster" +0.800000 0x3f4ccccd "UH" +0.800000 0x3f4ccccd "extrablack" +0.800000 0x3f4ccccd "nord" +0.800000 0x3f4ccccd "poster" +0.800000 0x3f4ccccd 951 - 999 +0.800000 0x3f4ccccd panose 11 +0.850000 0x3f59999a "Obese" +0.850000 0x3f59999a "obese" diff --git a/doc/export/ctweightsraw b/doc/export/ctweightsraw index d34cf0e0..0eda5021 100644 --- a/doc/export/ctweightsraw +++ b/doc/export/ctweightsraw @@ -76,17 +76,16 @@ 0.620000 0x3f1eb852 9, 801 - 900 0.750000 0x3f400000 901 - 950 0.800000 0x3f4ccccd 951 - 999 --0.500000 0xbf000000 2 --0.400000 0xbecccccd 3 -of true 4 --0.230000 0xbe6b851f 5 -0.230000 0x3e6b851f 6 -0.250000 0x3e800000 7 -0.400000 0x3ecccccd 8 -0.560000 0x3f0f5c29 9 -0.620000 0x3f1eb852 10 -0.800000 0x3f4ccccd 11 -0.400000 0x3ecccccd 1 +-0.500000 0xbf000000 panose 2 +-0.400000 0xbecccccd panose 3 +-0.230000 0xbe6b851f panose 5 +0.230000 0x3e6b851f panose 6 +0.250000 0x3e800000 panose 7 +0.400000 0x3ecccccd panose 8 +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 panose 10 +0.800000 0x3f4ccccd panose 11 +0.400000 0x3ecccccd 'head'[0x2D] & 1 -0.200000 0xbe4ccccd "EL" 0.500000 0x3f000000 "EB" 0.300000 0x3e99999a "SB" diff --git a/doc/export/ctweightvalues b/doc/export/ctweightvalues index ee7f17dc..24f5fe91 100644 --- a/doc/export/ctweightvalues +++ b/doc/export/ctweightvalues @@ -84,19 +84,19 @@ registered font, OS2 weights; table length >= 78 901 - 950: float32as(0.750000, 0x3f400000) 951 - 999: float32as(0.800000, 0x3f4ccccd) 0, 1000: panose - 2: float32as(-0.500000, 0xbf000000) - 3: float32as(-0.400000, 0xbecccccd) - 4: !!!! should be float32as(-0.300000, 0xbe99999a) but is treated as invalid instead due to returning false instead of true - 5: float32as(-0.230000, 0xbe6b851f) - 6: float32as(0.230000, 0x3e6b851f) - 7: float32as(0.250000, 0x3e800000) - 8: float32as(0.400000, 0x3ecccccd) - 9: float32as(0.560000, 0x3f0f5c29) - 10: float32as(0.620000, 0x3f1eb852) - 11: float32as(0.800000, 0x3f4ccccd) +panose 2: float32as(-0.500000, 0xbf000000) +panose 3: float32as(-0.400000, 0xbecccccd) +panose 4: !!!! see note should be float32as(-0.300000, 0xbe99999a) but is treated as invalid instead due to returning false instead of true +panose 5: float32as(-0.230000, 0xbe6b851f) +panose 6: float32as(0.230000, 0x3e6b851f) +panose 7: float32as(0.250000, 0x3e800000) +panose 8: float32as(0.400000, 0x3ecccccd) +panose 9: float32as(0.560000, 0x3f0f5c29) +panose 10: float32as(0.620000, 0x3f1eb852) +panose 11: float32as(0.800000, 0x3f4ccccd) registered font, head table, low bit of byte 0x2D -1: float32as(0.400000, 0x3ecccccd) +'head'[0x2D] & 1: float32as(0.400000, 0x3ecccccd) registered font, abbreviated weight glossary, checks for (possibly in TrueType and OpenType only) in order: preferred subfamily, family; case-insensitive strict comparison "EL": float32as(-0.200000, 0xbe4ccccd) diff --git a/doc/export/weightlist3.sh b/doc/export/weightlist3.sh new file mode 100644 index 00000000..f772226b --- /dev/null +++ b/doc/export/weightlist3.sh @@ -0,0 +1,3 @@ +# 21 october 2017 +sort -t$'\t' -k1,1 -k2,2 "$@" | + column -t -s$'\t' From b2b5bc36b1a13c8691f47918583329c6e64f80c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:45:15 -0400 Subject: [PATCH 304/487] And annotated the ctweights list. --- doc/export/ctweightsannotated | 149 ++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 doc/export/ctweightsannotated diff --git a/doc/export/ctweightsannotated b/doc/export/ctweightsannotated new file mode 100644 index 00000000..23db5436 --- /dev/null +++ b/doc/export/ctweightsannotated @@ -0,0 +1,149 @@ +-0.100000 0xbdcccccd registered postscript name "STXihei" +-0.100000 0xbfb999999999999a unregistered postscript name "STXihei" +-0.200000 0xbe4ccccd registered subfamily abbr "EL" +-0.200000 0xbe4ccccd "Semi Light" +-0.200000 0xbe4ccccd "SemiLight" +-0.200000 0xbe4ccccd "semi light" +-0.200000 0xbe4ccccd "semilight" +-0.230000 0xbe6b851f "Book" +-0.230000 0xbe6b851f "W1" +-0.230000 0xbe6b851f "W3" +-0.230000 0xbe6b851f "book" +-0.230000 0xbe6b851f "w3" +-0.230000 0xbe6b851f panose 5 +-0.400000 0xbecccccd registered subfamily abbr "L" +-0.400000 0xbecccccd "Light" +-0.400000 0xbecccccd "Lite" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "lite" +-0.400000 0xbecccccd registered OS2 weights 3, 201 - 300 +-0.400000 0xbecccccd panose 3 +-0.500000 0xbf000000 "Ext Light" +-0.500000 0xbf000000 "Extra Light" +-0.500000 0xbf000000 "ExtraLight" +-0.500000 0xbf000000 "Thin" +-0.500000 0xbf000000 "W2" +-0.500000 0xbf000000 "extra light" +-0.500000 0xbf000000 "extralight" +-0.500000 0xbf000000 "thin" +-0.500000 0xbf000000 "w2" +-0.500000 0xbf000000 registered OS2 weights 2, 101 - 200 +-0.500000 0xbf000000 panose 2 +-0.600000 0xbf19999a "thin" +-0.700000 0xbf333333 "hairline" +-0.700000 0xbf333333 "w1" +-0.800000 0xbf4ccccd "Ext Thin" +-0.800000 0xbf4ccccd "Extra Thin" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ultra Light" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "UltraLight" +-0.800000 0xbf4ccccd "ulight" +-0.800000 0xbf4ccccd "ultra light" +-0.800000 0xbf4ccccd "ultralight" +-0.800000 0xbf4ccccd registered OS2 weights 1, 10 - 100 +0.000000 0x0 registered postscript name ".Keyboard" +0.000000 0x0 registered postscript name ".LucidaGrandeUI" +0.000000 0x0 unregistered postscript name ".LucidaGrandeUI" +0.000000 0x0 registered postscript name "LucidaGrande" +0.000000 0x0 unregistered postscript name "LucidaGrande" +0.000000 0x0 registered subfamily abbr "R" +0.000000 0x0 registered postscript name "TimesNewRomanPSMT" +0.000000 0x0 unregistered postscript name "TimesNewRomanPSMT" +0.000000 0x0 "W4" +0.000000 0x0 "reg" +0.000000 0x0 "w4" +0.000000 0x0 registered OS2 weights 4, 301 - 400 +0.000000 0x0 default +0.230000 0x3e6b851 registered OS2 weights 5, 401 - 500 +0.230000 0x3e6b851f registered subfamily abbr "M" +0.230000 0x3e6b851f "Medium" +0.230000 0x3e6b851f "W5" +0.230000 0x3e6b851f "med" +0.230000 0x3e6b851f "medium" +0.230000 0x3e6b851f "w5" +0.230000 0x3e6b851f panose 6 +0.240000 0x3e75c28f registered postscript name "STHeiti" +0.240000 0x3fceb851eb851eb8 unregistered postscript name "STHeiti" +0.250000 0x3e800000 "Demi Bold" +0.250000 0x3e800000 "Demi" +0.250000 0x3e800000 "DemiBold" +0.250000 0x3e800000 "demi bold" +0.250000 0x3e800000 "demi" +0.250000 0x3e800000 "demibold" +0.250000 0x3e800000 registered OS2 weights 6, 501 - 600 +0.250000 0x3e800000 panose 7 +0.300000 0x3e99999a registered subfamily abbr "SB" +0.300000 0x3e99999a "Semi Bold" +0.300000 0x3e99999a "Semi" +0.300000 0x3e99999a "SemiBold" +0.300000 0x3e99999a "W6" +0.300000 0x3e99999a "semi bold" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semibold" +0.300000 0x3e99999a "w6" +0.400000 0x3ecccccd registered subfamily abbr "B" +0.400000 0x3ecccccd "Bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd 'head'[0x2D] & 1 +0.400000 0x3ecccccd registered OS2 weights 7, 601 - 700 +0.400000 0x3ecccccd panose 8 +0.440000 0x3ee147ae "W7" +0.440000 0x3ee147ae "w7" +0.500000 0x3f000000 registered subfamily abbr "EB" +0.500000 0x3f000000 "Ext Bold" +0.500000 0x3f000000 "Extra Bold" +0.500000 0x3f000000 "Extra" +0.500000 0x3f000000 "ExtraBold" +0.500000 0x3f000000 "Ultra" +0.500000 0x3f000000 "extra bold" +0.500000 0x3f000000 "extra" +0.500000 0x3f000000 "extrabold" +0.500000 0x3f000000 registered OS2 weights 8, 701 - 800 +0.540000 0x3f0a3d71 "W8" +0.540000 0x3f0a3d71 "w8" +0.560000 0x3f0f5c29 registered subfamily abbr "H" +0.560000 0x3f0f5c29 "Heavy Face" +0.560000 0x3f0f5c29 "Heavy" +0.560000 0x3f0f5c29 "HeavyFace" +0.560000 0x3f0f5c29 "heavy face" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavyface" +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.620000 0x3f1eb852 "W9" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.620000 0x3f1eb852 "w9" +0.620000 0x3f1eb852 registered OS2 weights 9, 801 - 900 +0.620000 0x3f1eb852 panose 10 +0.700000 0x3f333333 registered subfamily abbr "U" +0.700000 0x3f333333 "Ultra Bold" +0.700000 0x3f333333 "UltraBold" +0.700000 0x3f333333 "ultra bold" +0.700000 0x3f333333 "ultrabold" +0.750000 0x3f400000 "Fat" +0.750000 0x3f400000 "Ultra Black" +0.750000 0x3f400000 "UltraBlack" +0.750000 0x3f400000 "fat" +0.750000 0x3f400000 "ultra black" +0.750000 0x3f400000 "ultrablack" +0.750000 0x3f400000 registered OS2 weights 901 - 950 +0.800000 0x3f4ccccd "Ext Black" +0.800000 0x3f4ccccd "ExtraBlack" +0.800000 0x3f4ccccd "Nord" +0.800000 0x3f4ccccd "Poster" +0.800000 0x3f4ccccd registered subfamily abbr "UH" +0.800000 0x3f4ccccd "extrablack" +0.800000 0x3f4ccccd "nord" +0.800000 0x3f4ccccd "poster" +0.800000 0x3f4ccccd registered OS2 weights 951 - 999 +0.800000 0x3f4ccccd panose 11 +0.850000 0x3f59999a "Obese" +0.850000 0x3f59999a "obese" From e186c0e69a96ea1c82a8c41c13d72769d731c9f2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 22:43:43 -0400 Subject: [PATCH 305/487] Mapped out Core Text width value deduction. Will need to do the other files for this one too. --- doc/export/ctwidths | 160 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 doc/export/ctwidths diff --git a/doc/export/ctwidths b/doc/export/ctwidths new file mode 100644 index 00000000..dd6d2ca2 --- /dev/null +++ b/doc/export/ctwidths @@ -0,0 +1,160 @@ +xx pseudo-go + +func (f *CTFont) RegistryWidth32() float32 { + metadata visual descriptors + { "med", float32as(0.000000, 0x0) }, + { "cond", float32as(-0.200000, 0xbe4ccccd) }, + { "ext", float32as(0.200000, 0x3e4ccccd) }, + + style dictionary + { "Extra Compressed", float32as(-0.700000, 0xbf333333) }, + { "Ultra Compressed", float32as(-0.700000, 0xbf333333) }, + { "Ultra Condensed", float32as(-0.700000, 0xbf333333) }, + { "Extra Condensed", float32as(-0.500000, 0xbf000000) }, + { "Extra Extended", float32as(0.400000, 0x3ecccccd) }, + { "Ext Compressed", float32as(-0.700000, 0xbf333333) }, + { "Ultra Expanded", float32as(0.800000, 0x3f4ccccd) }, + { "Ultra Extended", float32as(0.800000, 0x3f4ccccd) }, + { "Extra Expanded", float32as(0.400000, 0x3ecccccd) }, + xx TODO this is weird, but correct... + { "Semi Condensed", float32as(-0.700000, 0xbf333333) }, + { "Semi Condensed", float32as(-0.100000, 0xbdcccccd) }, + xx end TODO + { "Ext Condensed", float32as(-0.500000, 0xbf000000) }, + { "SemiCondensed", float32as(-0.100000, 0xbdcccccd) }, + { "ExtraExpanded", float32as(0.400000, 0x3ecccccd) }, + { "Semi Expanded", float32as(0.100000, 0x3dcccccd) }, + { "Semi Extended", float32as(0.100000, 0x3dcccccd) }, + { "Ext Expanded", float32as(0.400000, 0x3ecccccd) }, + { "Ext Extended", float32as(0.400000, 0x3ecccccd) }, + { "SemiExpanded", float32as(0.100000, 0x3dcccccd) }, + { "Extra Narrow", float32as(-0.500000, 0xbf000000) }, + { "ExtraNarrow", float32as(-0.500000, 0xbf000000) }, + { "Extra Wide", float32as(0.800000, 0x3f4ccccd) }, + { "Ultra Cond", float32as(-0.700000, 0xbf333333) }, + { "Compressed", float32as(-0.500000, 0xbf000000) }, + { "Extra Cond", float32as(-0.500000, 0xbf000000) }, + { "Semi Cond", float32as(-0.100000, 0xbdcccccd) }, + { "Condensed", float32as(-0.200000, 0xbe4ccccd) }, + { "ExtraWide", float32as(0.800000, 0x3f4ccccd) }, + { "Extended", float32as(0.200000, 0x3e4ccccd) }, + { "Expanded", float32as(0.200000, 0x3e4ccccd) }, + { "Ext Cond", float32as(-0.500000, 0xbf000000) }, + { "Narrow", float32as(-0.400000 , 0xbecccccd) }, + { "Compact", float32as(-0.400000, 0xbecccccd) }, + { "Cond", float32as(-0.200000, 0xbe4ccccd) }, + { "Wide", float32as(0.600000, 0x3f19999a) }, + { "Thin", float32as(-0.700000, 0xbf333333) }, + + get os2 table + if os2table.Len() >= 78 { + usWidthClass := uint16be(b[6:8]) - 1 + xx this handles the case where the original usWidthClass == 0 + if usWidthClass > 8 { + panose := b[0x23] - 2 + if panose > 6 { + xx TODO + } else { + switch panose { + case 0, 1, 2: + width = float32as(0.000000, 0x0) + case 3: + width = float32as(0.200000, 0x3e4ccccd) + case 4: + width = float32as(-0.200000, 0xbe4ccccd) + case 5: + width = float32as(0.400000, 0x3ecccccd) + case 6: + width = float32as(-0.400000, 0xbecccccd) + } + } + } + switch usWidthClass { + case 0: + width = float32as(-0.700000, 0xbf333333) + case 1: + width = float32as(-0.500000, 0xbf000000) + case 2: + width = float32as(-0.200000, 0xbe4ccccd) + case 3: + width = float32as(-0.100000, 0xbdcccccd) + case 4: + width = float32as(0.000000, 0x0) + case 5: + width = float32as(0.100000, 0x3dcccccd) + case 6: + width = float32as(0.400000, 0x3ecccccd) + case 7: + width = float32as(0.600000, 0x3f19999a) + case 8: + width = float32as(0.800000, 0x3f4ccccd) + } + } + + headtable := f.CopyTable('head') + if headtable != nil { + if headtable.Len() >= 54 { + x := b[0x2d] + if (x & 0x20) != 0 { + width = float32as(-0.200000, 0xbe4ccccd) + } else if (x & 0x40) != 0 { + width = float32as(0.200000, 0x3e4ccccd) + } + } + } + + xx and if all else fails + return float32as(0.000000, 0x0) +} + +func (f *CTFont) Width() float64 { + if f.IsRegistered() { + return f.RegistryWidth() + } + + width := 0.0 + hasWidth := false + + if there is an OS2 table { + var usWidthClass uint16 + + valid := false + if it's 78 bytes or more { + usWidthClass = uint16be(table[6:8]) + if usWeightClass <= 10 { + valid = true + } else { + valid = false + } + } else { + usWidthClass = 0 + valid = true + } + if valid { + ten := float64as(10.000000, 0x4024000000000000) + negPointFive := float64as(-0.500000, 0xbfe0000000000000) + width = (float64(usWidthClass) div ten) + negPointFive + hasWidth = true + } + } + + then there's the style glossary strings comparison: + { "semi condensed", float32as(-0.100000, 0xbdcccccd) }, + { "extra expanded", float32as(0.400000, 0x3ecccccd) }, + { "semicondensed", float32as(-0.100000, 0xbdcccccd) }, + { "extraexpanded", float32as(0.400000, 0x3ecccccd) }, + { "semi expanded", float32as(0.100000, 0x3dcccccd) }, + { "semiexpanded", float32as(0.100000, 0x3dcccccd) }, + { "extra narrow", float32as(-0.500000, 0xbf000000) }, + { "extranarrow", float32as(-0.500000, 0xbf000000) }, + { "extra wide", float32as(0.800000, 0x3f4ccccd) }, + { "condensed", float32as(-0.200000, 0xbe4ccccd) }, + { "extrawide", float32as(0.800000, 0x3f4ccccd) }, + { "extended", float32as(0.200000, 0x3e4ccccd) }, + { "expanded", float32as(0.200000, 0x3e4ccccd) }, + { "narrow", float32as(-0.400000, 0xbecccccd) }, + { "wide", float32as(0.600000, 0x3f19999a) }, + { "thin", float32as(-0.700000, 0xbf333333) }, + + otherwise just return float64as(0.000000, 0x0) +} From e2369df648c0860c2557ce665a25b1c4797e5f91 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 23:55:03 -0400 Subject: [PATCH 306/487] More width stuff. --- doc/export/ctwidthvalues | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 doc/export/ctwidthvalues diff --git a/doc/export/ctwidthvalues b/doc/export/ctwidthvalues new file mode 100644 index 00000000..fbccfc3f --- /dev/null +++ b/doc/export/ctwidthvalues @@ -0,0 +1,112 @@ +metadata "med": float32as(0.000000, 0x0) +metadata "cond": float32as(-0.200000, 0xbe4ccccd) +metadata "ext": float32as(0.200000, 0x3e4ccccd) + +registered "Extra Compressed": float32as(-0.700000, 0xbf333333) +registered "Ultra Compressed": float32as(-0.700000, 0xbf333333) +registered "Ultra Condensed": float32as(-0.700000, 0xbf333333) +registered "Extra Condensed": float32as(-0.500000, 0xbf000000) +registered "Extra Extended": float32as(0.400000, 0x3ecccccd) +registered "Ext Compressed": float32as(-0.700000, 0xbf333333) +registered "Ultra Expanded": float32as(0.800000, 0x3f4ccccd) +registered "Ultra Extended": float32as(0.800000, 0x3f4ccccd) +registered "Extra Expanded": float32as(0.400000, 0x3ecccccd) +registered "Semi Condensed": float32as(-0.700000, 0xbf333333) +registered "Semi Condensed": float32as(-0.100000, 0xbdcccccd) +registered "Ext Condensed": float32as(-0.500000, 0xbf000000) +registered "SemiCondensed": float32as(-0.100000, 0xbdcccccd) +registered "ExtraExpanded": float32as(0.400000, 0x3ecccccd) +registered "Semi Expanded": float32as(0.100000, 0x3dcccccd) +registered "Semi Extended": float32as(0.100000, 0x3dcccccd) +registered "Ext Expanded": float32as(0.400000, 0x3ecccccd) +registered "Ext Extended": float32as(0.400000, 0x3ecccccd) +registered "SemiExpanded": float32as(0.100000, 0x3dcccccd) +registered "Extra Narrow": float32as(-0.500000, 0xbf000000) +registered "ExtraNarrow": float32as(-0.500000, 0xbf000000) +registered "Extra Wide": float32as(0.800000, 0x3f4ccccd) +registered "Ultra Cond": float32as(-0.700000, 0xbf333333) +registered "Compressed": float32as(-0.500000, 0xbf000000) +registered "Extra Cond": float32as(-0.500000, 0xbf000000) +registered "Semi Cond": float32as(-0.100000, 0xbdcccccd) +registered "Condensed": float32as(-0.200000, 0xbe4ccccd) +registered "ExtraWide": float32as(0.800000, 0x3f4ccccd) +registered "Extended": float32as(0.200000, 0x3e4ccccd) +registered "Expanded": float32as(0.200000, 0x3e4ccccd) +registered "Ext Cond": float32as(-0.500000, 0xbf000000) +registered "Narrow": float32as(-0.400000 , 0xbecccccd) +registered "Compact": float32as(-0.400000, 0xbecccccd) +registered "Cond": float32as(-0.200000, 0xbe4ccccd) +registered "Wide": float32as(0.600000, 0x3f19999a) +registered "Thin": float32as(-0.700000, 0xbf333333) + +panose 2, 3, 4: float32as(0.000000, 0x0) +panose 5: float32as(0.200000, 0x3e4ccccd) +panose 6: float32as(-0.200000, 0xbe4ccccd) +panose 7: float32as(0.400000, 0x3ecccccd) +panose 8: float32as(-0.400000, 0xbecccccd) + +registered OS2 1: float32as(-0.700000, 0xbf333333) +registered OS2 2: float32as(-0.500000, 0xbf000000) +registered OS2 3: float32as(-0.200000, 0xbe4ccccd) +registered OS2 4: float32as(-0.100000, 0xbdcccccd) +registered OS2 5: float32as(0.000000, 0x0) +registered OS2 6: float32as(0.100000, 0x3dcccccd) +registered OS2 7: float32as(0.400000, 0x3ecccccd) +registered OS2 8: float32as(0.600000, 0x3f19999a) +registered OS2 9: float32as(0.800000, 0x3f4ccccd) + +head[0x2d] & 0x20: float32as(-0.200000, 0xbe4ccccd) +head[0x2d] & 0x40: float32as(0.200000, 0x3e4ccccd) + +registered default: float32as(0.000000, 0x0) + +func (f *CTFont) Width() float64 { + if f.IsRegistered() { + return f.RegistryWidth() + } + + width := 0.0 + hasWidth := false + + if there is an OS2 table { + var usWidthClass uint16 + + valid := false + if it's 78 bytes or more { + usWidthClass = uint16be(table[6:8]) + if usWeightClass <= 10 { + valid = true + } else { + valid = false + } + } else { + usWidthClass = 0 + valid = true + } + if valid { + ten := float64as(10.000000, 0x4024000000000000) + negPointFive := float64as(-0.500000, 0xbfe0000000000000) + width = (float64(usWidthClass) div ten) + negPointFive + hasWidth = true + } + } + + then there's the style glossary strings comparison: +unregistered "semi condensed": float32as(-0.100000, 0xbdcccccd) +unregistered "extra expanded": float32as(0.400000, 0x3ecccccd) +unregistered "semicondensed": float32as(-0.100000, 0xbdcccccd) +unregistered "extraexpanded": float32as(0.400000, 0x3ecccccd) +unregistered "semi expanded": float32as(0.100000, 0x3dcccccd) +unregistered "semiexpanded": float32as(0.100000, 0x3dcccccd) +unregistered "extra narrow": float32as(-0.500000, 0xbf000000) +unregistered "extranarrow": float32as(-0.500000, 0xbf000000) +unregistered "extra wide": float32as(0.800000, 0x3f4ccccd) +unregistered "condensed": float32as(-0.200000, 0xbe4ccccd) +unregistered "extrawide": float32as(0.800000, 0x3f4ccccd) +unregistered "extended": float32as(0.200000, 0x3e4ccccd) +unregistered "expanded": float32as(0.200000, 0x3e4ccccd) +unregistered "narrow": float32as(-0.400000, 0xbecccccd) +unregistered "wide": float32as(0.600000, 0x3f19999a) +unregistered "thin": float32as(-0.700000, 0xbf333333) + +default: float64as(0.000000, 0x0) From 1851fc80459e799f8a816ff0e14b1c1986398892 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Oct 2017 01:27:57 -0400 Subject: [PATCH 307/487] Added code to print the unregistered OS2 width values using the real instructions and constants used by Core Text. --- doc/export/writewidths.c | 3 +++ doc/export/writewidths.out | 11 ++++++++ doc/export/writewidths.s | 51 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 doc/export/writewidths.c create mode 100644 doc/export/writewidths.out create mode 100644 doc/export/writewidths.s diff --git a/doc/export/writewidths.c b/doc/export/writewidths.c new file mode 100644 index 00000000..2ba7d01b --- /dev/null +++ b/doc/export/writewidths.c @@ -0,0 +1,3 @@ +// 22 october 2017 +extern int realMain(void); +int main(void) { return realMain(); } diff --git a/doc/export/writewidths.out b/doc/export/writewidths.out new file mode 100644 index 00000000..83f6b413 --- /dev/null +++ b/doc/export/writewidths.out @@ -0,0 +1,11 @@ +unregistered OS2 0: float64as(-0.5, 0xbfe0000000000000) +unregistered OS2 1: float64as(-0.4, 0xbfd999999999999a) +unregistered OS2 2: float64as(-0.3, 0xbfd3333333333333) +unregistered OS2 3: float64as(-0.2, 0xbfc999999999999a) +unregistered OS2 4: float64as(-0.1, 0xbfb9999999999998) +unregistered OS2 5: float64as(0, 0x0000000000000000) +unregistered OS2 6: float64as(0.1, 0x3fb9999999999998) +unregistered OS2 7: float64as(0.2, 0x3fc9999999999998) +unregistered OS2 8: float64as(0.3, 0x3fd3333333333334) +unregistered OS2 9: float64as(0.4, 0x3fd999999999999a) +unregistered OS2 10: float64as(0.5, 0x3fe0000000000000) diff --git a/doc/export/writewidths.s b/doc/export/writewidths.s new file mode 100644 index 00000000..d10208d5 --- /dev/null +++ b/doc/export/writewidths.s @@ -0,0 +1,51 @@ +# 22 october 2017 +# clang -o writewidths writewidths.c writewidths.s -g -Wall -Wextra -pedantic -g +# thanks to: +# - http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/ +# - https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/Assembler/060-i386_Addressing_Modes_and_Assembler_Instructions/i386_intructions.html#//apple_ref/doc/uid/TP30000825-TPXREF101 +# - https://stackoverflow.com/questions/46309041/trivial-macos-assembly-64-bit-program-has-incorrect-stack-alignment +# - https://www.google.com/search?q=macos+implement+main+in+assembly+-nasm&oq=macos+implement+main+in+assembly+-nasm&gs_l=psy-ab.3...12877.13839.0.13988.6.6.0.0.0.0.117.407.4j1.5.0....0...1.1.64.psy-ab..1.0.0....0.et6MkokjvwA +# - https://stackoverflow.com/questions/2529185/what-are-cfi-directives-in-gnu-assembler-gas-used-for + +.section __DATA,__data + +double10: + .quad 0x4024000000000000 +doubleNeg05: + .quad 0xbfe0000000000000 + +fmt: + .asciz "unregistered OS2 %ld:\tfloat64as(%g, 0x%016lx)\n" + +.section __TEXT,__text +.globl _realMain +_realMain: + pushq %rbp + movq %rsp, %rbp + addq $8, %rsp + + xorq %rcx, %rcx +loop: + pushq %rcx + # the code from core text + movzwl %cx, %ecx + xorps %xmm0, %xmm0 + cvtsi2sdl %ecx, %xmm0 + divsd double10(%rip), %xmm0 + addsd doubleNeg05(%rip), %xmm0 + # end core text code + popq %rcx + pushq %rcx + movd %xmm0, %rdx + movzwq %cx, %rsi + leaq fmt(%rip), %rdi + callq _printf + popq %rcx + incw %cx + cmpw $10, %cx + jbe loop + + xorq %rax, %rax + subq $8, %rsp + popq %rbp + ret From 1673156fd67b96a1785e55829d58d043c37ce7c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Oct 2017 01:31:26 -0400 Subject: [PATCH 308/487] And the last bits for widths. --- doc/export/ctwidthsprocessed | 149 +++++++++++++++++++++++++++++++ doc/export/writewidths.processed | 11 +++ 2 files changed, 160 insertions(+) create mode 100644 doc/export/ctwidthsprocessed create mode 100644 doc/export/writewidths.processed diff --git a/doc/export/ctwidthsprocessed b/doc/export/ctwidthsprocessed new file mode 100644 index 00000000..d3ec018f --- /dev/null +++ b/doc/export/ctwidthsprocessed @@ -0,0 +1,149 @@ +-0.100000 0xbdcccccd "STXihei" +-0.100000 0xbfb999999999999a "STXihei" +-0.200000 0xbe4ccccd "EL" +-0.200000 0xbe4ccccd "Semi Light" +-0.200000 0xbe4ccccd "SemiLight" +-0.200000 0xbe4ccccd "semi light" +-0.200000 0xbe4ccccd "semilight" +-0.230000 0xbe6b851f "Book" +-0.230000 0xbe6b851f "W1" +-0.230000 0xbe6b851f "W3" +-0.230000 0xbe6b851f "book" +-0.230000 0xbe6b851f "w3" +-0.230000 0xbe6b851f panose 5 +-0.400000 0xbecccccd "L" +-0.400000 0xbecccccd "Light" +-0.400000 0xbecccccd "Lite" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "lite" +-0.400000 0xbecccccd 3, 201 - 300 +-0.400000 0xbecccccd panose 3 +-0.500000 0xbf000000 "Ext Light" +-0.500000 0xbf000000 "Extra Light" +-0.500000 0xbf000000 "ExtraLight" +-0.500000 0xbf000000 "Thin" +-0.500000 0xbf000000 "W2" +-0.500000 0xbf000000 "extra light" +-0.500000 0xbf000000 "extralight" +-0.500000 0xbf000000 "thin" +-0.500000 0xbf000000 "w2" +-0.500000 0xbf000000 2, 101 - 200 +-0.500000 0xbf000000 panose 2 +-0.600000 0xbf19999a "thin" +-0.700000 0xbf333333 "hairline" +-0.700000 0xbf333333 "w1" +-0.800000 0xbf4ccccd "Ext Thin" +-0.800000 0xbf4ccccd "Extra Thin" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ultra Light" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "UltraLight" +-0.800000 0xbf4ccccd "ulight" +-0.800000 0xbf4ccccd "ultra light" +-0.800000 0xbf4ccccd "ultralight" +-0.800000 0xbf4ccccd 1, 10 - 100 +0.000000 0x0 ".Keyboard" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "R" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "W4" +0.000000 0x0 "reg" +0.000000 0x0 "w4" +0.000000 0x0 4, 301 - 400 +0.000000 0x0 default +0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f "M" +0.230000 0x3e6b851f "Medium" +0.230000 0x3e6b851f "W5" +0.230000 0x3e6b851f "med" +0.230000 0x3e6b851f "medium" +0.230000 0x3e6b851f "w5" +0.230000 0x3e6b851f panose 6 +0.240000 0x3e75c28f "STHeiti" +0.240000 0x3fceb851eb851eb8 "STHeiti" +0.250000 0x3e800000 "Demi Bold" +0.250000 0x3e800000 "Demi" +0.250000 0x3e800000 "DemiBold" +0.250000 0x3e800000 "demi bold" +0.250000 0x3e800000 "demi" +0.250000 0x3e800000 "demibold" +0.250000 0x3e800000 6, 501 - 600 +0.250000 0x3e800000 panose 7 +0.300000 0x3e99999a "SB" +0.300000 0x3e99999a "Semi Bold" +0.300000 0x3e99999a "Semi" +0.300000 0x3e99999a "SemiBold" +0.300000 0x3e99999a "W6" +0.300000 0x3e99999a "semi bold" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semibold" +0.300000 0x3e99999a "w6" +0.400000 0x3ecccccd "B" +0.400000 0x3ecccccd "Bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd 'head'[0x2D] & 1 +0.400000 0x3ecccccd 7, 601 - 700 +0.400000 0x3ecccccd panose 8 +0.440000 0x3ee147ae "W7" +0.440000 0x3ee147ae "w7" +0.500000 0x3f000000 "EB" +0.500000 0x3f000000 "Ext Bold" +0.500000 0x3f000000 "Extra Bold" +0.500000 0x3f000000 "Extra" +0.500000 0x3f000000 "ExtraBold" +0.500000 0x3f000000 "Ultra" +0.500000 0x3f000000 "extra bold" +0.500000 0x3f000000 "extra" +0.500000 0x3f000000 "extrabold" +0.500000 0x3f000000 8, 701 - 800 +0.540000 0x3f0a3d71 "W8" +0.540000 0x3f0a3d71 "w8" +0.560000 0x3f0f5c29 "H" +0.560000 0x3f0f5c29 "Heavy Face" +0.560000 0x3f0f5c29 "Heavy" +0.560000 0x3f0f5c29 "HeavyFace" +0.560000 0x3f0f5c29 "heavy face" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavyface" +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.620000 0x3f1eb852 "W9" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.620000 0x3f1eb852 "w9" +0.620000 0x3f1eb852 9, 801 - 900 +0.620000 0x3f1eb852 panose 10 +0.700000 0x3f333333 "U" +0.700000 0x3f333333 "Ultra Bold" +0.700000 0x3f333333 "UltraBold" +0.700000 0x3f333333 "ultra bold" +0.700000 0x3f333333 "ultrabold" +0.750000 0x3f400000 "Fat" +0.750000 0x3f400000 "Ultra Black" +0.750000 0x3f400000 "UltraBlack" +0.750000 0x3f400000 "fat" +0.750000 0x3f400000 "ultra black" +0.750000 0x3f400000 "ultrablack" +0.750000 0x3f400000 901 - 950 +0.800000 0x3f4ccccd "Ext Black" +0.800000 0x3f4ccccd "ExtraBlack" +0.800000 0x3f4ccccd "Nord" +0.800000 0x3f4ccccd "Poster" +0.800000 0x3f4ccccd "UH" +0.800000 0x3f4ccccd "extrablack" +0.800000 0x3f4ccccd "nord" +0.800000 0x3f4ccccd "poster" +0.800000 0x3f4ccccd 951 - 999 +0.800000 0x3f4ccccd panose 11 +0.850000 0x3f59999a "Obese" +0.850000 0x3f59999a "obese" diff --git a/doc/export/writewidths.processed b/doc/export/writewidths.processed new file mode 100644 index 00000000..e0437d81 --- /dev/null +++ b/doc/export/writewidths.processed @@ -0,0 +1,11 @@ +-0.1 0xbfb9999999999998 unregistered OS2 4 +-0.2 0xbfc999999999999a unregistered OS2 3 +-0.3 0xbfd3333333333333 unregistered OS2 2 +-0.4 0xbfd999999999999a unregistered OS2 1 +-0.5 0xbfe0000000000000 unregistered OS2 0 +0 0x0000000000000000 unregistered OS2 5 +0.1 0x3fb9999999999998 unregistered OS2 6 +0.2 0x3fc9999999999998 unregistered OS2 7 +0.3 0x3fd3333333333334 unregistered OS2 8 +0.4 0x3fd999999999999a unregistered OS2 9 +0.5 0x3fe0000000000000 unregistered OS2 10 From 78bfc623993858842f6e503192fb1856638571cb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Oct 2017 10:06:28 -0400 Subject: [PATCH 309/487] Oops; generated weights instead of widths. --- doc/export/ctwidthsprocessed | 222 ++++++++++++----------------------- 1 file changed, 73 insertions(+), 149 deletions(-) diff --git a/doc/export/ctwidthsprocessed b/doc/export/ctwidthsprocessed index d3ec018f..26f9fcc2 100644 --- a/doc/export/ctwidthsprocessed +++ b/doc/export/ctwidthsprocessed @@ -1,149 +1,73 @@ --0.100000 0xbdcccccd "STXihei" --0.100000 0xbfb999999999999a "STXihei" --0.200000 0xbe4ccccd "EL" --0.200000 0xbe4ccccd "Semi Light" --0.200000 0xbe4ccccd "SemiLight" --0.200000 0xbe4ccccd "semi light" --0.200000 0xbe4ccccd "semilight" --0.230000 0xbe6b851f "Book" --0.230000 0xbe6b851f "W1" --0.230000 0xbe6b851f "W3" --0.230000 0xbe6b851f "book" --0.230000 0xbe6b851f "w3" --0.230000 0xbe6b851f panose 5 --0.400000 0xbecccccd "L" --0.400000 0xbecccccd "Light" --0.400000 0xbecccccd "Lite" --0.400000 0xbecccccd "light" --0.400000 0xbecccccd "light" --0.400000 0xbecccccd "lite" --0.400000 0xbecccccd 3, 201 - 300 --0.400000 0xbecccccd panose 3 --0.500000 0xbf000000 "Ext Light" --0.500000 0xbf000000 "Extra Light" --0.500000 0xbf000000 "ExtraLight" --0.500000 0xbf000000 "Thin" --0.500000 0xbf000000 "W2" --0.500000 0xbf000000 "extra light" --0.500000 0xbf000000 "extralight" --0.500000 0xbf000000 "thin" --0.500000 0xbf000000 "w2" --0.500000 0xbf000000 2, 101 - 200 --0.500000 0xbf000000 panose 2 --0.600000 0xbf19999a "thin" --0.700000 0xbf333333 "hairline" --0.700000 0xbf333333 "w1" --0.800000 0xbf4ccccd "Ext Thin" --0.800000 0xbf4ccccd "Extra Thin" --0.800000 0xbf4ccccd "HairLine" --0.800000 0xbf4ccccd "Ultra Light" --0.800000 0xbf4ccccd "Ultra Thin" --0.800000 0xbf4ccccd "UltraLight" --0.800000 0xbf4ccccd "ulight" --0.800000 0xbf4ccccd "ultra light" --0.800000 0xbf4ccccd "ultralight" --0.800000 0xbf4ccccd 1, 10 - 100 -0.000000 0x0 ".Keyboard" -0.000000 0x0 ".LucidaGrandeUI" -0.000000 0x0 ".LucidaGrandeUI" -0.000000 0x0 "LucidaGrande" -0.000000 0x0 "LucidaGrande" -0.000000 0x0 "R" -0.000000 0x0 "TimesNewRomanPSMT" -0.000000 0x0 "TimesNewRomanPSMT" -0.000000 0x0 "W4" -0.000000 0x0 "reg" -0.000000 0x0 "w4" -0.000000 0x0 4, 301 - 400 -0.000000 0x0 default -0.230000 0x3e6b851 5, 401 - 500 -0.230000 0x3e6b851f "M" -0.230000 0x3e6b851f "Medium" -0.230000 0x3e6b851f "W5" -0.230000 0x3e6b851f "med" -0.230000 0x3e6b851f "medium" -0.230000 0x3e6b851f "w5" -0.230000 0x3e6b851f panose 6 -0.240000 0x3e75c28f "STHeiti" -0.240000 0x3fceb851eb851eb8 "STHeiti" -0.250000 0x3e800000 "Demi Bold" -0.250000 0x3e800000 "Demi" -0.250000 0x3e800000 "DemiBold" -0.250000 0x3e800000 "demi bold" -0.250000 0x3e800000 "demi" -0.250000 0x3e800000 "demibold" -0.250000 0x3e800000 6, 501 - 600 -0.250000 0x3e800000 panose 7 -0.300000 0x3e99999a "SB" -0.300000 0x3e99999a "Semi Bold" -0.300000 0x3e99999a "Semi" -0.300000 0x3e99999a "SemiBold" -0.300000 0x3e99999a "W6" -0.300000 0x3e99999a "semi bold" -0.300000 0x3e99999a "semi" -0.300000 0x3e99999a "semi" -0.300000 0x3e99999a "semibold" -0.300000 0x3e99999a "w6" -0.400000 0x3ecccccd "B" -0.400000 0x3ecccccd "Bold" -0.400000 0x3ecccccd "bold" -0.400000 0x3ecccccd "bold" -0.400000 0x3ecccccd 'head'[0x2D] & 1 -0.400000 0x3ecccccd 7, 601 - 700 -0.400000 0x3ecccccd panose 8 -0.440000 0x3ee147ae "W7" -0.440000 0x3ee147ae "w7" -0.500000 0x3f000000 "EB" -0.500000 0x3f000000 "Ext Bold" -0.500000 0x3f000000 "Extra Bold" -0.500000 0x3f000000 "Extra" -0.500000 0x3f000000 "ExtraBold" -0.500000 0x3f000000 "Ultra" -0.500000 0x3f000000 "extra bold" -0.500000 0x3f000000 "extra" -0.500000 0x3f000000 "extrabold" -0.500000 0x3f000000 8, 701 - 800 -0.540000 0x3f0a3d71 "W8" -0.540000 0x3f0a3d71 "w8" -0.560000 0x3f0f5c29 "H" -0.560000 0x3f0f5c29 "Heavy Face" -0.560000 0x3f0f5c29 "Heavy" -0.560000 0x3f0f5c29 "HeavyFace" -0.560000 0x3f0f5c29 "heavy face" -0.560000 0x3f0f5c29 "heavy" -0.560000 0x3f0f5c29 "heavy" -0.560000 0x3f0f5c29 "heavyface" -0.560000 0x3f0f5c29 panose 9 -0.620000 0x3f1eb852 "Black" -0.620000 0x3f1eb852 "Super" -0.620000 0x3f1eb852 "W9" -0.620000 0x3f1eb852 "black" -0.620000 0x3f1eb852 "black" -0.620000 0x3f1eb852 "super" -0.620000 0x3f1eb852 "w9" -0.620000 0x3f1eb852 9, 801 - 900 -0.620000 0x3f1eb852 panose 10 -0.700000 0x3f333333 "U" -0.700000 0x3f333333 "Ultra Bold" -0.700000 0x3f333333 "UltraBold" -0.700000 0x3f333333 "ultra bold" -0.700000 0x3f333333 "ultrabold" -0.750000 0x3f400000 "Fat" -0.750000 0x3f400000 "Ultra Black" -0.750000 0x3f400000 "UltraBlack" -0.750000 0x3f400000 "fat" -0.750000 0x3f400000 "ultra black" -0.750000 0x3f400000 "ultrablack" -0.750000 0x3f400000 901 - 950 -0.800000 0x3f4ccccd "Ext Black" -0.800000 0x3f4ccccd "ExtraBlack" -0.800000 0x3f4ccccd "Nord" -0.800000 0x3f4ccccd "Poster" -0.800000 0x3f4ccccd "UH" -0.800000 0x3f4ccccd "extrablack" -0.800000 0x3f4ccccd "nord" -0.800000 0x3f4ccccd "poster" -0.800000 0x3f4ccccd 951 - 999 -0.800000 0x3f4ccccd panose 11 -0.850000 0x3f59999a "Obese" -0.850000 0x3f59999a "obese" +-0.100000 0xbdcccccd registered "Semi Cond" +-0.100000 0xbdcccccd registered "Semi Condensed" +-0.100000 0xbdcccccd registered "SemiCondensed" +-0.100000 0xbdcccccd registered OS2 4 +-0.100000 0xbdcccccd unregistered "semi condensed" +-0.100000 0xbdcccccd unregistered "semicondensed" +-0.200000 0xbe4ccccd head[0x2d] & 0x20 +-0.200000 0xbe4ccccd metadata "cond" +-0.200000 0xbe4ccccd panose 6 +-0.200000 0xbe4ccccd registered "Cond" +-0.200000 0xbe4ccccd registered "Condensed" +-0.200000 0xbe4ccccd registered OS2 3 +-0.200000 0xbe4ccccd unregistered "condensed" +-0.400000 0xbecccccd panose 8 +-0.400000 0xbecccccd registered "Compact" +-0.400000 0xbecccccd registered "Narrow" +-0.400000 0xbecccccd unregistered "narrow" +-0.500000 0xbf000000 registered "Compressed" +-0.500000 0xbf000000 registered "Ext Cond" +-0.500000 0xbf000000 registered "Ext Condensed" +-0.500000 0xbf000000 registered "Extra Cond" +-0.500000 0xbf000000 registered "Extra Condensed" +-0.500000 0xbf000000 registered "Extra Narrow" +-0.500000 0xbf000000 registered "ExtraNarrow" +-0.500000 0xbf000000 registered OS2 2 +-0.500000 0xbf000000 unregistered "extra narrow" +-0.500000 0xbf000000 unregistered "extranarrow" +-0.700000 0xbf333333 registered "Ext Compressed" +-0.700000 0xbf333333 registered "Extra Compressed" +-0.700000 0xbf333333 registered "Semi Condensed" +-0.700000 0xbf333333 registered "Thin" +-0.700000 0xbf333333 registered "Ultra Compressed" +-0.700000 0xbf333333 registered "Ultra Cond" +-0.700000 0xbf333333 registered "Ultra Condensed" +-0.700000 0xbf333333 registered OS2 1 +-0.700000 0xbf333333 unregistered "thin" +0.000000 0x0 default +0.000000 0x0 metadata "med" +0.000000 0x0 panose 2, 3, 4 +0.000000 0x0 registered OS2 5 +0.000000 0x0 registered default +0.100000 0x3dcccccd registered "Semi Expanded" +0.100000 0x3dcccccd registered "Semi Extended" +0.100000 0x3dcccccd registered "SemiExpanded" +0.100000 0x3dcccccd registered OS2 6 +0.100000 0x3dcccccd unregistered "semi expanded" +0.100000 0x3dcccccd unregistered "semiexpanded" +0.200000 0x3e4ccccd head[0x2d] & 0x40 +0.200000 0x3e4ccccd metadata "ext" +0.200000 0x3e4ccccd panose 5 +0.200000 0x3e4ccccd registered "Expanded" +0.200000 0x3e4ccccd registered "Extended" +0.200000 0x3e4ccccd unregistered "expanded" +0.200000 0x3e4ccccd unregistered "extended" +0.400000 0x3ecccccd panose 7 +0.400000 0x3ecccccd registered "Ext Expanded" +0.400000 0x3ecccccd registered "Ext Extended" +0.400000 0x3ecccccd registered "Extra Expanded" +0.400000 0x3ecccccd registered "Extra Extended" +0.400000 0x3ecccccd registered "ExtraExpanded" +0.400000 0x3ecccccd registered OS2 7 +0.400000 0x3ecccccd unregistered "extra expanded" +0.400000 0x3ecccccd unregistered "extraexpanded" +0.600000 0x3f19999a registered "Wide" +0.600000 0x3f19999a registered OS2 8 +0.600000 0x3f19999a unregistered "wide" +0.800000 0x3f4ccccd registered "Extra Wide" +0.800000 0x3f4ccccd registered "ExtraWide" +0.800000 0x3f4ccccd registered "Ultra Expanded" +0.800000 0x3f4ccccd registered "Ultra Extended" +0.800000 0x3f4ccccd registered OS2 9 +0.800000 0x3f4ccccd unregistered "extra wide" +0.800000 0x3f4ccccd unregistered "extrawide" From 48594e3cd155e4fe85388b130e49982ada2672cf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 Oct 2017 11:44:29 -0400 Subject: [PATCH 310/487] More TODOs. --- TODO.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO.md b/TODO.md index 7a783af9..6389f1f8 100644 --- a/TODO.md +++ b/TODO.md @@ -254,3 +254,6 @@ mac os x accessibility - https://developer.apple.com/documentation/appkit/nsworkspace/1524656-accessibilitydisplayshoulddiffer?language=objc - https://developer.apple.com/documentation/appkit/nsworkspace/1526290-accessibilitydisplayshouldincrea?language=objc - https://developer.apple.com/documentation/appkit/nsworkspace/1533006-accessibilitydisplayshouldreduce?language=objc + +uiEntry disabling bugs http://www.cocoabuilder.com/archive/cocoa/215525-nstextfield-bug-can-be.html +uiMultilineEntry disabling https://developer.apple.com/library/content/qa/qa1461/_index.html From 0a1b5ff6a867409c207f4c6516fe7d7ab6e5b185 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 31 Oct 2017 22:48:02 -0400 Subject: [PATCH 311/487] Oops, accidentally chopped off a hex digit from one of the ctweights values. Fixed (manually). --- doc/export/ctweights | 2 +- doc/export/ctweightsannotated | 2 +- doc/export/ctweightsprocessed | 2 +- doc/export/ctweightsraw | 2 +- doc/export/ctweightvalues | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index f9feb90b..0bdbd0d4 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -318,7 +318,7 @@ do401AndUp: } return float32as(0.250000, 0x3e800000), true } - return float32as(0.230000, 0x3e6b851), true + return float32as(0.230000, 0x3e6b851f), true do601AndUp: if usWeightClass > 700 { diff --git a/doc/export/ctweightsannotated b/doc/export/ctweightsannotated index 23db5436..8df56f25 100644 --- a/doc/export/ctweightsannotated +++ b/doc/export/ctweightsannotated @@ -56,7 +56,7 @@ 0.000000 0x0 "w4" 0.000000 0x0 registered OS2 weights 4, 301 - 400 0.000000 0x0 default -0.230000 0x3e6b851 registered OS2 weights 5, 401 - 500 +0.230000 0x3e6b851f registered OS2 weights 5, 401 - 500 0.230000 0x3e6b851f registered subfamily abbr "M" 0.230000 0x3e6b851f "Medium" 0.230000 0x3e6b851f "W5" diff --git a/doc/export/ctweightsprocessed b/doc/export/ctweightsprocessed index d3ec018f..fe315cc7 100644 --- a/doc/export/ctweightsprocessed +++ b/doc/export/ctweightsprocessed @@ -56,7 +56,7 @@ 0.000000 0x0 "w4" 0.000000 0x0 4, 301 - 400 0.000000 0x0 default -0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f 5, 401 - 500 0.230000 0x3e6b851f "M" 0.230000 0x3e6b851f "Medium" 0.230000 0x3e6b851f "W5" diff --git a/doc/export/ctweightsraw b/doc/export/ctweightsraw index 0eda5021..502d473e 100644 --- a/doc/export/ctweightsraw +++ b/doc/export/ctweightsraw @@ -69,7 +69,7 @@ -0.500000 0xbf000000 2, 101 - 200 -0.400000 0xbecccccd 3, 201 - 300 0.000000 0x0 4, 301 - 400 -0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f 5, 401 - 500 0.250000 0x3e800000 6, 501 - 600 0.400000 0x3ecccccd 7, 601 - 700 0.500000 0x3f000000 8, 701 - 800 diff --git a/doc/export/ctweightvalues b/doc/export/ctweightvalues index 24f5fe91..acbcb794 100644 --- a/doc/export/ctweightvalues +++ b/doc/export/ctweightvalues @@ -76,7 +76,7 @@ registered font, OS2 weights; table length >= 78 2, 101 - 200: float32as(-0.500000, 0xbf000000) 3, 201 - 300: float32as(-0.400000, 0xbecccccd) 4, 301 - 400: float32as(0.000000, 0x0) -5, 401 - 500: float32as(0.230000, 0x3e6b851) +5, 401 - 500: float32as(0.230000, 0x3e6b851f) 6, 501 - 600: float32as(0.250000, 0x3e800000) 7, 601 - 700: float32as(0.400000, 0x3ecccccd) 8, 701 - 800: float32as(0.500000, 0x3f000000) From b769b8163985ecf1d30cd614b5f31e349adc00d4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 31 Oct 2017 23:49:45 -0400 Subject: [PATCH 312/487] Added an aggregation of Core Text weight values for my own thinking and planning. --- doc/export/ctweightscombined | 146 +++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 doc/export/ctweightscombined diff --git a/doc/export/ctweightscombined b/doc/export/ctweightscombined new file mode 100644 index 00000000..d5017938 --- /dev/null +++ b/doc/export/ctweightscombined @@ -0,0 +1,146 @@ +-0.100000 + postscript name "STXihei" (which, according to http://www.typophile.com/node/93813, means "ST Hei Light", and so we can assume it's the Light version of STHeiti, which I think is Medium though is given Regular status below because meh) + +-0.200000 + subfamily abbreviation "EL" (???????????) + style string contains "Semi Light" + style string contains "SemiLight" + +-0.230000 + style string contains "Book" + style string contains "W1" (registered fonts only; probably a typo and -0.7 was expected instead) + style string contains "W3" + panose 5 + +-0.400000 + subfamily abbreviation "L" + style string contains "Light" + style string contains "Lite" + ATSD style "light" + OS2 weights 3, 201 - 300 + panose 3 + +-0.500000 + style string contains "Ext Light" + style string contains "Extra Light" + style string contains "ExtraLight" + style string contains "Thin" + style string contains "W2" + OS2 weights 2, 101 - 200 + panose 2 + +-0.600000 + ATSD style "thin" + +-0.700000 + style string contains "hairline" + style string contains "w1" + +-0.800000 + style string contains "Ext Thin" + style string contains "Extra Thin" + style string contains "HairLine" + style string contains "Ultra Light" + style string contains "Ultra Thin" + style string contains "UltraLight" + ATSD style "ulight" + OS2 weights 1, 10 - 100 + +0.000000 + ostscript name ".Keyboard" + postscript name ".LucidaGrandeUI" + postscript name "LucidaGrande" + subfamily abbreviation "R" + postscript name "TimesNewRomanPSMT" + style string contains "W4" + ATSD style "reg" + OS2 weights 4, 301 - 400 + default + +0.230000 + OS2 weights 5, 401 - 500 + subfamily abbreviation "M" + style string contains "Medium" + style string contains "W5" + ATSD style "med" + panose 6 + +0.240000 + postscript name "STHeiti" + +0.250000 + style string contains "Demi Bold" + style string contains "Demi" + style string contains "DemiBold" + OS2 weights 6, 501 - 600 + panose 7 + +0.300000 + subfamily abbreviation "SB" + style string contains "Semi Bold" + style string contains "Semi" + style string contains "SemiBold" + style string contains "W6" + ATSD style "semi" + +0.400000 + subfamily abbreviation "B" + style string contains "Bold" + ATSD style "bold" + ('head' table byte 0x2D) & 1 != 0 + OS2 weights 7, 601 - 700 + panose 8 + +0.440000 + style string contains "W7" + +0.500000 + subfamily abbreviation "EB" + style string contains "Ext Bold" + style string contains "Extra Bold" + style string contains "Extra" + style string contains "ExtraBold" + style string contains "Ultra" + OS2 weights 8, 701 - 800 + +0.540000 + style string contains "W8" + +0.560000 + subfamily abbreviation "H" + style string contains "Heavy Face" + style string contains "Heavy" + style string contains "HeavyFace" + ATSD style "heavy" + panose 9 + +0.620000 + style string contains "Black" + style string contains "Super" + style string contains "W9" + ATSD style "black" + OS2 weights 9, 801 - 900 + panose 10 + +0.700000 + subfamily abbreviation "U" + style string contains "Ultra Bold" + style string contains "UltraBold" + +0.750000 + style string contains "Fat" + style string contains "Ultra Black" + style string contains "UltraBlack" + OS2 weights 901 - 950 + +0.800000 + style string contains "Ext Black" + style string contains "ExtraBlack" + style string contains "Nord" + style string contains "Poster" + subfamily abbreviation "UH" + OS2 weights 951 - 999 + panose 11 + +0.850000 + style string contains "Obese" From 2481610ee46f879864cd4825f02bd33bb60a68d4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 13:34:54 -0400 Subject: [PATCH 313/487] And made a similar ctwidthscombined to the previous commit's ctweightscombined. Now I can start thinking about implementation. --- doc/export/ctwidthscombined | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 doc/export/ctwidthscombined diff --git a/doc/export/ctwidthscombined b/doc/export/ctwidthscombined new file mode 100644 index 00000000..fe4ad9d9 --- /dev/null +++ b/doc/export/ctwidthscombined @@ -0,0 +1,77 @@ +-0.100000 + style string contains "Semi Cond" + style string contains "Semi Condensed"; unregistered fonts only (see below) + style string contains "SemiCondensed" + OS2 width 4 + +-0.200000 + ('head' table byte 0x2d) & 0x20 != 0 + ATSD style "cond" + panose 6 + style string contains "Cond" + style string contains "Condensed" + OS2 width 3 + +-0.400000 + panose 8 + style string contains "Compact" + style string contains "Narrow" + +-0.500000 + style string contains "Compressed" + style string contains "Ext Cond" + style string contains "Ext Condensed" + style string contains "Extra Cond" + style string contains "Extra Condensed" + style string contains "Extra Narrow" + style string contains "ExtraNarrow" + OS2 width 2 + +-0.700000 + style string contains "Ext Compressed" + style string contains "Extra Compressed" + style string contains "Semi Condensed" (this is probably a typo, since another "Semi Condensed" with a value of -0.1 follows this in the table it comes from); registered fonts only + style string contains "Thin" + style string contains "Ultra Compressed" + style string contains "Ultra Cond" + style string contains "Ultra Condensed" + OS2 width 1 + +0.000000 + default + ATSD style "med" + panose 2, 3, 4 + OS2 width 5 + +0.100000 + style string contains "Semi Expanded" + style string contains "Semi Extended" + style string contains "SemiExpanded" + OS2 width 6 + +0.200000 + ('head' table byte 0x2d) & 0x40 != 0 + ATSD style "ext" + panose 5 + style string contains "Expanded" + style string contains "Extended" + +0.400000 + panose 7 + style string contains "Ext Expanded" + style string contains "Ext Extended" + style string contains "Extra Expanded" + style string contains "Extra Extended" + style string contains "ExtraExpanded" + OS2 width 7 + +0.600000 + style string contains "Wide" + OS2 width 8 + +0.800000 + style string contains "Extra Wide" + style string contains "ExtraWide" + style string contains "Ultra Expanded" + style string contains "Ultra Extended" + OS2 width 9 From 3108f65b394b792897c9cb0617db86c59580d698 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 20:14:44 -0400 Subject: [PATCH 314/487] Wrote the initial version of the final code for converting Core Text traits into libui traits. --- doc/export/fonttraits.m | 315 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 doc/export/fonttraits.m diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m new file mode 100644 index 00000000..f3a90816 --- /dev/null +++ b/doc/export/fonttraits.m @@ -0,0 +1,315 @@ +// 1 november 2017 +#import "uipriv_darwin.h" + +static BOOL fontRegistered(CTFontDescriptorRef desc) +{ + CFNumberRef scope; + CTFontManagerScope val; + + scope = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); + if (scope == NULL) + // header says this should be treated as the same as not registered + return NO; + if (CFNumberGetValue(scope, kCFNumberSInt32Type, &val) == false) { + // TODO + CFRelease(scope); + return NO; + } + CFRelease(scope); + // examination of Core Text shows this is accurate + return val != kCTFontManagerScopeNone; +} + +static BOOL getTraits(CTFontDescriptorRef desc, CTFontSymbolicTraits *symbolic, double *weight, double *width) +{ + CFDictionaryRef traits = NULL; + CFNumberRef num = NULL; + + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute); + if (traits == NULL) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (num == NULL) + goto fail; + if (CFNumberGetValue(num, kCFNumberSInt32Type, symbolic) == false) + goto fail; + + num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWeightTrait); + if (num == NULL) + goto fail; + if (CFNumberGetValue(num, kCFNumberDoubleType, weight) == false) + goto fail; + + num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWidthTrait); + if (num == NULL) + goto fail; + if (CFNumberGetValue(num, kCFNumberDoubleType, width) == false) + goto fail; + + CFRelease(traits); + return YES; + +fail: + CFRelease(traits); + return NO; +} + +// Core Text doesn't seem to differentiate between Italic and Oblique. +// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess +static uiDrawTextFontItalic guessItalicOblique(CTFontDescriptorRef desc) +{ + CFStringRef styleName; + BOOL isOblique; + + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + if (styleName != NULL) { + CFRange range; + + range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return uiDrawFontItalicOblique; + return uiDrawFontItalicItalic; +} + +// Core Text does (usWidthClass - 0.5) x 10 +// this roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves +// we'll just treat them as identical to 1 and 9, respectively +static const uiDrawFontStretch os2WidthsToStretches[] = { + uiDrawTextStretchUltraCondensed, + uiDrawTextStretchUltraCondensed, + uiDrawTextStretchExtraCondensed, + uiDrawTextStretchCondensed, + uiDrawTextStretchSemiCondensed, + uiDrawTextStretchNormal, + uiDrawTextStretchSemiExpanded, + uiDrawTextStretchExpanded, + uiDrawTextStretchExtraExpanded, + uiDrawTextStretchUltraExpanded, + uiDrawTextStretchUltraExpanded, +}; + +static const CFStringRef exceptions[] = { + CFSTR("LucidaGrande"), + CFSTR(".LucidaGrandeUI"), + CFSTR("STHeiti"), + CFSTR("STXihei"), + CFSTR("TimesNewRomanPSMT"), + NULL, +}; + +static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +{ + CTFontRef font; + CFDataRef os2; + uint16_t usWeightClass, usWidthClass; + CFStringRef psname; + CFStringRef *ex; + + *hasWeight = NO; + *hasWidth = NO; + + // only applies to unregistered fonts + if (fontRegistered(desc)) + return; + + font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); + + os2 = CTFontCopyTable(font, kCTFontTableOS2, kCTFontTableOptionNoOptions); + if (os2 == NULL) { + // no OS2 table, so no secondary values + CFRelease(font); + return; + } + + if (CFDataGetLength(os2) > 77) { + const UInt8 *b; + + b = CFDataGetBytePtr(os2); + + usWeightClass = ((uint16_t) (b[4])) << 8; + usWeightClass |= (uint16_t) (b[5]); + if (usWeightClass <= 1000) { + if (usWeightClass < 11) + usWeigthClass *= 100; + *hasWeight = YES; + } + + usWidthClass = ((uint16_t) (b[6])) << 8; + usWidthClass |= (uint16_t) (b[7]); + if (usWidthClass <= 10) + *hasWidth = YES; + } else { + usWeightClass = 0; + *hasWeight = YES; + + usWidthClass = 0; + *hasWidth = YES; + } + if (*hasWeight) + // we can just use this directly + out->Weight = usWeightClass; + if (*hasWidth) + out->Stretch = os2WidthsToStretches[usWidthClass]; + CFRelease(os2); + + // don't use secondary weights in the event of special predefined names + psname = CTFontCopyPostScriptName(font); + for (ex = exceptions; *ex != NULL; ex++) + if (CFEqual(psname, *ex)) { + *hasWeight = NO; + break; + } + CFRelease(psname); + + CFRelease(font); +} + +static const CFStringRef subfamilyKeys[] = { + kCTFontSubFamilyNameKey, + // TODO explicitly mark these as undocumented + CFSTR("CTFontPreferredSubFamilyName"), + kCTFontFullNameKey, + CFSTR("CTFontPreferredFamilyName"), + kCTFontFamilyNameKey, + NULL, +}; + +static BOOL testTTFOTFSubfamilyNames(CTFontDescriptorRef desc, CFStringRef want) +{ + CFNumberRef num; + CTFontFormat type; + CGFontRef font; + CFString *key; + + num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontFormatAttribute); + if (num == NULL) { + // TODO + return NO; + } + if (CFNumberGetValue(num, kCFNumberSInt32Type, &type) == false) { + // TODO + CFRelease(num); + return NO; + } + CFRelease(num); + switch (type) { + case kCTFontFormatOpenTypePostScript: + case kCTFontFormatOpenTypeTrueType: + case kCTFontFormatTrueType: + break; + default: + return NO; + } + + font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); + for (key = subfamilyKeys; *key != NULL; key++) { + CFStringRef val; + CFRange range; + + val = CTFontCopyName(font, *key); + if (val == NULL) + continue; + range.location = 0; + range.length = CFStringGetLength(val); + if (CFStringFindWithOptions(val, want, range, + (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false) { + CFRelease(val); + CFRelease(font); + return YES; + } + CFRelease(val); + } + CFRelease(font); + return NO; +} + +// work around a bug in libFontRegistry.dylib +static BOOL shouldReallyBeThin(CTFontDescriptorRef desc) +{ + return testTTFOTFSubfamilyNames(desc, CFSTR("W1")); +} + +// work around a bug in libFontRegistry.dylib +static BOOL shouldReallyBeSemiCondensed(CTFontDescriptorRef desc) +{ + return testTTFOTFSubfamilyNames(desc, CFSTR("Semi Condensed")); +} + +void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) +{ + CTFontSymbolicTraits symbolic; + double weight; + double width; + BOOL hasWeight, hasWidth; + uint16_t usWeightClasss, usWidthClass; + CTFontRef font; + + if (!getTraits(desc, &symbolic, &weight, &width)) { + // TODO + goto fail; + } + + out->Italic = uiDrawTextItalicNormal; + if ((symbolic & kCTFontItalicTrait) != 0) + out->Italic = guessItalicOblique(desc); + + hasWeight = NO; + hasWidth = NO; + trySecondaryOS2Values(desc, out, &hasWeight, &hasWidth); + + if (!hasWeight) + // TODO this scale is a bit lopsided + if (weight <= -0.7) + out->Weight = uiDrawTextWeightThin; + else if (weight <= -0.5) + out->Weight = uiDrawTextWeightUltraLight; + else if (weight <= -0.3) + out->Weight = uiDrawTextWeightLight; + else if (weight <= -0.23) { + out->Weight = uiDrawTextWeightBook; + if (shouldReallyBeThin(desc)) + out->Weight = uiDrawTextWeightThin; + } else if (weight <= 0.0) + out->Weight = uiDrawTextWeightNormal; + else if (weight <= 0.23) + out->Weight = uiDrawTextWeightMedium; + else if (weight <= 0.3) + out->Weight = uiDrawTextWeightSemiBold; + else if (weight <= 0.4) + out->Weight = uiDrawTextWeightBold; + else if (weight <= 0.5) + out->Weight = uiDrawTextWeightUltraBold; + else if (weight <= 0.7) + out->Weight = uiDrawTextWeightHeavy; + else + out->Weight = uiDrawTextWeightUltraHeavy; + + if (!hasWidth) + // TODO this scale is a bit lopsided + if (width <= -0.7) { + out->Stretch = uiDrawTextStretchUltraCondensed; + if (shouldReallyBeSemiCondensed(desc)) + out->Stretch = uiDrawTextStretchSemiCondensed; + } else if (width <= -0.5) + out->Stretch = uiDrawTextStretchExtraCondensed; + else if (width <= -0.2) + out->Stretch = uiDrawTextStretchCondensed; + else if (width <= -0.1) + out->Stretch = uiDrawTextStretchSemiCondensed; + else if (width <= 0.0) + out->Stretch = uiDrawTextStretchNormal; + else if (width <= 0.1) + out->Stretch = uiDrawTextStretchSemiExpanded; + else if (width <= 0.2) + out->Stretch = uiDrawTextStretchExpanded; + else if (width <= 0.6) + out->Stretch = uiDrawTextStretchExtraExpanded; + else + out->Stretch = uiDrawTextStretchUltraExpanded; +} From a7bbbc8bb9e304ce2de80ebecb8aafc44d2d74cd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 20:20:12 -0400 Subject: [PATCH 315/487] Changed a slight thing in fonttraits.m. --- doc/export/fonttraits.m | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index f3a90816..1f49fc65 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -170,12 +170,15 @@ static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor CFRelease(font); } +// TODO explicitly mark these as undocumented +extern const CFStringRef kCTFontPreferredSubFamilyNameKey; +extern const CFStringRef kCTFontPreferredFamilyNameKey; + static const CFStringRef subfamilyKeys[] = { kCTFontSubFamilyNameKey, - // TODO explicitly mark these as undocumented - CFSTR("CTFontPreferredSubFamilyName"), + kCTFontPreferredSubFamilyNameKey, kCTFontFullNameKey, - CFSTR("CTFontPreferredFamilyName"), + kCTFontPreferredFamilyNameKey, kCTFontFamilyNameKey, NULL, }; From 6b295b2d3f0a58dd539234154b47e0604990bc2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 20:23:52 -0400 Subject: [PATCH 316/487] And fixed a comment placeholder (and typo) and reformatted it slightly. --- doc/export/fonttraits.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 1f49fc65..10aa45ea 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -77,9 +77,9 @@ static uiDrawTextFontItalic guessItalicOblique(CTFontDescriptorRef desc) return uiDrawFontItalicItalic; } -// Core Text does (usWidthClass - 0.5) x 10 -// this roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves -// we'll just treat them as identical to 1 and 9, respectively +// Core Text does (usWidthClass / 10) - 0.5 here. +// This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. +// We'll just treat them as identical to 1 and 9, respectively. static const uiDrawFontStretch os2WidthsToStretches[] = { uiDrawTextStretchUltraCondensed, uiDrawTextStretchUltraCondensed, From 03b089c972eaa84b409f7b06b1a33a38c1a63851 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 02:46:16 -0400 Subject: [PATCH 317/487] Added a fvar table test. This might be a bit more complicated... --- doc/export/fvar.swift | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 doc/export/fvar.swift diff --git a/doc/export/fvar.swift b/doc/export/fvar.swift new file mode 100644 index 00000000..74f02395 --- /dev/null +++ b/doc/export/fvar.swift @@ -0,0 +1,61 @@ +// 2 november 2017 +import Cocoa +import CoreText + +func fourccString(_ k: FourCharCode) -> String { + var c: [UInt8] = [0, 0, 0, 0] + c[0] = UInt8((k >> 24) & 0xFF) + c[1] = UInt8((k >> 16) & 0xFF) + c[2] = UInt8((k >> 8) & 0xFF) + c[3] = UInt8(k & 0xFF) + return String(bytes: c, encoding: .utf8)! +} + +var weightMin: Double = 0 +var weightMax: Double = 0 +var weightDef: Double = 0 +var weightVals: [String: Double] = [:] + +let attrs: [String: String] = [ + kCTFontFamilyNameAttribute as String: "Skia", +] +let bd = CTFontDescriptorCreateWithAttributes(attrs as CFDictionary) +let matches = CTFontDescriptorCreateMatchingFontDescriptors(bd, nil) as! [CTFontDescriptor] +let mfont = CTFontCreateWithFontDescriptor(matches[0], 0.0, nil) +let master = CTFontCopyVariationAxes(mfont) as! [NSDictionary] +master.forEach { d in + print("axis {") + d.forEach { k, v in + if (k as! String) == (kCTFontVariationAxisIdentifierKey as String) { + let c = v as! FourCharCode + print("\t\(k) \(fourccString(c))") + } else { + print("\t\(k) \(v)") + } + } + print("}") + if (d[kCTFontVariationAxisNameKey] as! String) == "Weight" { + weightMin = d[kCTFontVariationAxisMinimumValueKey] as! Double + weightMax = d[kCTFontVariationAxisMaximumValueKey] as! Double + weightDef = d[kCTFontVariationAxisDefaultValueKey] as! Double + } +} +print("") +matches.forEach { d in + let f = CTFontDescriptorCopyAttribute(d, kCTFontVariationAttribute) as! [FourCharCode: Double] + let n = CTFontDescriptorCopyAttribute(d, kCTFontStyleNameAttribute) as! String + print("\(n) {") + f.forEach { k, v in + print("\t\(fourccString(k)) \(v)") + } + print("}") + weightVals[n] = weightDef + if let v = f[2003265652] { + weightVals[n] = v + } +} +print("") +weightVals.forEach { k, v in + let scaled = (v - weightMin) / (weightMax - weightMin) + print("\(k) scaled = \(scaled) (OS2 \(UInt16(scaled * 1000)))") +} From 830753d888a66c556d6dc56cb26c7d145a5b23c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 03:17:33 -0400 Subject: [PATCH 318/487] More fvar.swift stuff. This is gonna hurt... --- doc/export/fvar.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/export/fvar.swift b/doc/export/fvar.swift index 74f02395..30617e45 100644 --- a/doc/export/fvar.swift +++ b/doc/export/fvar.swift @@ -56,6 +56,16 @@ matches.forEach { d in } print("") weightVals.forEach { k, v in - let scaled = (v - weightMin) / (weightMax - weightMin) - print("\(k) scaled = \(scaled) (OS2 \(UInt16(scaled * 1000)))") + let basicScaled = (v - weightMin) / (weightMax - weightMin) + print("\(k) basic scaled = \(basicScaled) (OS2 \(UInt16(basicScaled * 1000)))") + // https://www.microsoft.com/typography/otspec/otvaroverview.htm#CSN + var opentypeScaled: Double = 0 + if v < weightDef { + opentypeScaled = -((weightDef - v) / (weightDef - weightMin)) + } else if v > weightDef { + opentypeScaled = (v - weightDef) / (weightMax - weightDef) + } + print("\(k) opentype scaled = \(opentypeScaled)") } +print("") +print("\(CTFontDescriptorCreateMatchingFontDescriptors(CTFontDescriptorCreateCopyWithVariation(matches[0], FourCharCode(2003265652) as CFNumber, CGFloat(weightMax)), Set([kCTFontVariationAttribute as String]) as CFSet))") From 2e5f2c273b0b69629fc85f185797b970c8504bab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 10:39:43 -0400 Subject: [PATCH 319/487] More fvar.swift stuff. I'm going to need a font with an avar table... --- doc/export/fvar.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/export/fvar.swift b/doc/export/fvar.swift index 30617e45..3d81c9d8 100644 --- a/doc/export/fvar.swift +++ b/doc/export/fvar.swift @@ -68,4 +68,6 @@ weightVals.forEach { k, v in print("\(k) opentype scaled = \(opentypeScaled)") } print("") -print("\(CTFontDescriptorCreateMatchingFontDescriptors(CTFontDescriptorCreateCopyWithVariation(matches[0], FourCharCode(2003265652) as CFNumber, CGFloat(weightMax)), Set([kCTFontVariationAttribute as String]) as CFSet))") +print("\(String(describing: CTFontDescriptorCreateMatchingFontDescriptors(CTFontDescriptorCreateCopyWithVariation(matches[0], FourCharCode(2003265652) as CFNumber, CGFloat(weightMax)), Set([kCTFontVariationAttribute as String]) as CFSet)))") +print("") +print("\(CTFontCopyTable(mfont, CTFontTableTag(kCTFontTableAvar), []) != nil)") From b7e6311621957f94d7893c7f4b58de0618a1920d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 19:27:57 -0400 Subject: [PATCH 320/487] Started writing the code for processing font variations for Core Text so we can process Skia correctly. --- doc/export/fontvariation.m | 161 +++++++++++++++++++++++++++++++++++++ doc/export/ttfixedtest.go | 58 +++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 doc/export/fontvariation.m create mode 100644 doc/export/ttfixedtest.go diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m new file mode 100644 index 00000000..58bae666 --- /dev/null +++ b/doc/export/fontvariation.m @@ -0,0 +1,161 @@ +// 2 november 2017 +#import "uipriv_darwin.h" + +// references: +// - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html +// - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html +// - https://www.microsoft.com/typography/otspec/fvar.htm +// - https://www.microsoft.com/typography/otspec/otvaroverview.htm#CSN +// - https://www.microsoft.com/typography/otspec/otff.htm +// - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#Types +// - https://www.microsoft.com/typography/otspec/avar.htm + +typedef uint32_t fixed1616; +typedef uint16_t fixed214; + +static fixed1616 doubleToFixed1616(double d) +{ + double ipart, fpart; + long flong; + int16_t i16; + uint32_t ret; + + fpart = fabs(modf(d, &ipart)); + fpart *= 65536; + flong = lround(fpart); + i16 = (int16_t) ipart; + ret = (uint32_t) ((uint16_t) i16); + ret <<= 16; + ret |= (uint16_t) (flong & 0xFFFF); + return (fixed1616) ret; +} + +static double fixed1616ToDouble(fixed1616 f) +{ + int16_t base; + double frac; + + base = (int16_t) ((f >> 16) & 0xFFFF); + frac = ((double) (f & 0xFFFF)) / 65536; + return ((double) base) + frac; +} + +static fixed214 fixed1616ToFixed214(fixed1616 f) +{ + uint32_t t; + uint32_t topbit; + + t = f + 0x00000002; + topbit = t & 0x80000000; + t >>= 2; + if (topbit != 0) + t |= 0xC000000; + return (fixed214) (t & 0xFFFF); +} + +static double fixed214ToDouble(fixed214 f) +{ + double base; + double frac; + + switch ((f >> 14) & 0x3) { + case 0: + base = 0; + break; + case 1: + base = 1; + break; + case 2: + base = -2: + break; + case 3: + base = -1; + } + frac = ((double) (f & 0x3FFF)) / 16384; + return base + frac; +} + +static fixed1616 fixed214ToFixed1616(fixed214 f) +{ + int32_t t; + uint32_t x; + + t = (int32_t) ((int16_t) f); + t <<= 2; + x = (uint32_t) t; + return (float1616) (x - 0x00000002); +} + +static const fixed1616Negative1 = 0xFFFF0000; +static const fixed1616Zero = 0x00000000; +static const fixed1616Positive1 = 0x00010000; + +static fixed1616 normalize1616(fixed1616 val, fixed1616 min, fixed1616 max, fixed1616 def) +{ + if (val < min) + val = min; + if (val > max) + val = max; + if (val < def) + return -(def - val) / (def - min); + if (val > def) + return (val - def) / (max - def); + return fixed1616Zero; +} + +static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, size_t avarCount) +{ + if (val < fixed1616Negative1) + val = fixed1616Negative1; + if (val > fixed1616Positive1) + val = fixed1616Positive1; + if (avarCount != 0) { + size_t start, end; + float1616 startFrom, endFrom; + float1616 startTo, endTo; + + for (end = 0; end < avarCount; end += 2) { + endFrom = avarMappings[end]; + endTo = avarMappings[end + 1]; + if (endFrom >= val) + break; + } + if (endFrom == val) + val = endTo; + else { + start = end - 2; + startFrom = avarMappings[start]; + startTo = avarMappings[start + 1]; + val = (val - startFrom) / (endFrom - startFrom); + val *= (endTo - startTo); + val += startTo; + } + } + return fixed1616ToFixed214(val); +} + +fixed1616 *avarExtract(CFDataRef table, size_t index, size_t *n) +{ + const UInt8 *b; + size_t off; + size_t i, nEntries; + fixed1616 *entries; + fixed1616 *p; + + b = CFDataGetBytePtr(table); + off = 8; +#define nextuint16be() ((((uint16_t) (b[off])) << 8) | ((uint16_t) (b[off + 1]))) + for (; index > 0; index--) { + nEntries = (size_t) nextuint16be(); + off += 2; + off += 4 * nEntries; + } + nEntries = nextuint16be(); + *n = nEntries * 2; + entries = (fixed1616 *) uiAlloc(*n * sizeof (fixed1616), "fixed1616[]"); + for (i = 0; i < *n; i++) { + *p++ = fixed214ToFixed1616((fixed214) nextuint16be()); + off += 2; + } + return entries; +} diff --git a/doc/export/ttfixedtest.go b/doc/export/ttfixedtest.go new file mode 100644 index 00000000..217afa50 --- /dev/null +++ b/doc/export/ttfixedtest.go @@ -0,0 +1,58 @@ +// 2 november 2017 +package main + +import ( + "fmt" +) + +type fixed1616 uint32 +type fixed214 uint16 + +func fixed1616To214(f fixed1616) fixed214 { + f += 0x00000002 + g := int32(f) >> 2 + return fixed214(uint32(g) & 0xFFFF) +} + +func (f fixed1616) In214Range() bool { + base := int16((f >> 16) & 0xFFFF) + return base >= -2 && base < 2 +} + +func (f fixed1616) String() string { + base := int16((f >> 16) & 0xFFFF) + frac := float64(f & 0xFFFF) / 65536 + res := float64(base) + frac + return fmt.Sprintf("%f 0x%08X", res, uint32(f)) +} + +func (f fixed214) String() string { + base := []int16{ + 0, + 1, + -2, + -1, + }[(f & 0xC000) >> 14] + frac := float64(f & 0x3FFF) / 16384 + res := float64(base) + frac + return fmt.Sprintf("%f 0x%04X", res, uint16(f)) +} + +func main() { + fmt.Println(fixed214(0x7fff)) + fmt.Println(fixed214(0x8000)) + fmt.Println(fixed214(0x4000)) + fmt.Println(fixed214(0xc000)) + fmt.Println(fixed214(0x7000)) + fmt.Println(fixed214(0x0000)) + fmt.Println(fixed214(0x0001)) + fmt.Println(fixed214(0xffff)) + + fmt.Println() + + for i := uint64(0x00000000); i <= 0xFFFFFFFF; i++ { + j := fixed1616(i) + if !j.In214Range() { continue } + fmt.Println(j, "->", fixed1616To214(j)) + } +} From f2971f637f11f9d8381bf705efc337d2ddf3544a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 19:33:41 -0400 Subject: [PATCH 321/487] Added fvar axis identifiers. --- doc/export/fontvariation.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index 58bae666..d97a6158 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -10,6 +10,9 @@ // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#Types // - https://www.microsoft.com/typography/otspec/avar.htm +#define fvarWeight 0x77676874 +#define fvarWidth 0x77647468 + typedef uint32_t fixed1616; typedef uint16_t fixed214; From 57a6ea0a77a233439b6c38a38e52bf5698fe24aa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 21:10:38 -0400 Subject: [PATCH 322/487] Moved fontmatch.m alongside the other font files as we prepare to combine everything. --- {darwin => doc/export}/fontmatch.m | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {darwin => doc/export}/fontmatch.m (100%) diff --git a/darwin/fontmatch.m b/doc/export/fontmatch.m similarity index 100% rename from darwin/fontmatch.m rename to doc/export/fontmatch.m From be6a07755b8b99eb219ebd63b9f235c89407b6af Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 21:51:00 -0400 Subject: [PATCH 323/487] Described what each of the three .m files are going to do. --- doc/export/fontmatch.m | 30 +++++++++++++++++++++++------- doc/export/fonttraits.m | 17 +++++++++++++++++ doc/export/fontvariation.m | 20 +++++++++++++++++++- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index abd38c32..fe2dceae 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -1,13 +1,29 @@ // 3 january 2017 #import "uipriv_darwin.h" -// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. -// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for -// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) -// - CTFontCreateWithFontDescriptor() -// - CTFontCreateCopyWithAttributes() -// We have to implement the closest match ourselves. -// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. +// Core Text exposes font style info in two forms: +// - Fonts with a QuickDraw GX font variation (fvar) table, a feature +// adopted by OpenType, expose variations directly. +// - All other fonts have Core Text normalize the font style info +// into a traits dictionary. +// Of course this setup doesn't come without its hiccups and +// glitches. In particular, not only are the exact rules not well +// defined, but also font matching doesn't work as we want it to +// (exactly how varies based on the way the style info is exposed). +// So we'll have to implement style matching ourselves. +// We can use Core Text's matching to get a complete list of +// *possible* matches, and then we can filter out the ones we don't +// want ourselves. +// +// To make things easier for us, we'll match by converting Core +// Text's values back into libui values. This allows us to also use the +// normalization code for filling in uiDrawFontDescriptors from +// Core Text fonts and font descriptors. +// +// Style matching needs to be done early in the font loading process; +// in particular, we have to do this before adding any features, +// because the descriptors returned by Core Text's own font +// matching won't have any. struct closeness { CFIndex index; diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 10aa45ea..0c57b9d4 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -1,6 +1,23 @@ // 1 november 2017 #import "uipriv_darwin.h" +// This is the part of the font style matching and normalization code +// that handles fonts that use a traits dictionary. +// +// Matching stupidity: Core Text requires an **exact match for the +// entire traits dictionary**, otherwise it will **drop ALL the traits**. +// +// Normalization stupidity: Core Text uses its own scaled values for +// weight and width, but the values are different if the font is not +// registered and if said font is TrueType or OpenType. The values +// for all other cases do have some named constants starting with +// OS X 10.11, but even these aren't very consistent in practice. +// +// Of course, none of this is documented anywhere, so I had to do +// both trial-and-error AND reverse engineering to figure out what's +// what. We'll just convert Core Text's values into libui constants +// and use those for matching. + static BOOL fontRegistered(CTFontDescriptorRef desc) { CFNumberRef scope; diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index d97a6158..79236613 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -1,7 +1,25 @@ // 2 november 2017 #import "uipriv_darwin.h" -// references: +// This is the part of the font style matching and normalization code +// that handles fonts that use the fvar table. +// +// Matching stupidity: Core Text **doesn't even bother** matching +// these, even if you tell it to do so explicitly. It'll always return +// all variations for a given font. +// +// Normalization stupidity: Core Text doesn't normalize the fvar +// table values for us, so we'll have to do it ourselves. Furthermore, +// Core Text doesn't provide an API for accessing the avar table, if +// any, so we must do so ourselves. (TODO does Core Text even +// follow the avar table if a font has it?) +// +// Thankfully, normalization is well-defined in both TrueType and +// OpenType and seems identical in both, so we can just normalize +// the values and then convert them linearly to libui values for +// matching. +// +// References: // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html // - https://www.microsoft.com/typography/otspec/fvar.htm From 4e7fb5e264aa84f16de198474c0504c844ee7f1b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 15:51:21 -0400 Subject: [PATCH 324/487] Added code that handles all the attribute stuff for a CTFontRef AND CTFontDescriptorRef for us; we'll use this throughout the various font style files instead of doing everything ourselves. --- doc/export/fontmatch.m | 257 +++++++++++++++++++++++++++++++++++++++++ doc/export/fontstyle.h | 56 +++++++++ 2 files changed, 313 insertions(+) create mode 100644 doc/export/fontstyle.h diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index fe2dceae..74cbe2e1 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -25,6 +25,263 @@ // because the descriptors returned by Core Text's own font // matching won't have any. +@implementation fontStyleData + +- (id)initWithFont:(CTFontRef)f +{ + self = [super init]; + if (self) { + self->font = f; + CFRetain(self->font); + self->desc = CTFontCopyDescriptor(self->font); + if (![self prepare]) { + [self release]; + return nil; + } + } + return self; +} + +- (id)initWithDescriptor:(CTFontDescriptorRef)d +{ + self = [super init]; + if (self) { + self->font = NULL; + self->desc = d; + CFRetain(self->desc); + if (![self prepare]) { + [self release]; + return nil; + } + } + return self; +} + +- (void)dealloc +{ +#define REL(x) if (x != NULL) { CFRelease(x); x = NULL; } + REL(self->variationAxes); + REL(self->familyName); + REL(self->preferredFamilyName); + REL(self->fullName); + REL(self->subFamilyName); + REL(self->preferredSubFamilyName); + REL(self->postScriptName); + REL(self->variations); + REL(self->styleName); + REL(self->traits); + CFRelease(self->desc); + REL(self->font); + [super dealloc]; +} + +- (BOOL)prepare +{ + CFNumberRef num; + + self->traits = NULL; + self->symbolic = 0; + self->weight = 0; + self->width = 0; + self->didStyleName = NO; + self->styleName = NULL; + self->didVariations = NO; + self->variations = NULL; + self->didRegistrationScope = NO; + self->hasRegistrationScope = NO; + self->registrationScope = 0; + self->didPostScriptName = NO; + self->postScriptName = NULL; + self->didFontFormat = NO; + self->fontFormat = 0; + self->didPreferredSubFamilyName = NO; + self->preferredSubFamilyName = NULL; + self->didSubFamilyName = NO; + self->subFamilyName = NULL; + self->didFullName = NO; + self->fullName = NULL; + self->didPreferredFamilyName = NO; + self->preferredFamilyName = NULL; + self->didFamilyName = NO; + self->familyName = NULL; + self->didVariationAxes = NO; + self->variationAxes = NULL; + + self->traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontTraitsAttribute); + if (self->traits == NULL) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(self->traits, kCTFontSymbolicTrait); + if (num == NULL) + return NO; + if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->symbolic)) == false) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(self->traits, kCTFontWeightTrait); + if (num == NULL) + return NO; + if (CFNumberGetValue(num, kCFNumberDoubleType, &(self->weight)) == false) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(self->traits, kCTFontWidthTrait); + if (num == NULL) + return NO; + if (CFNumberGetValue(num, kCFNumberDoubleType, &(self->width)) == false) + return NO; + + return YES; +} + +- (void)ensureFont +{ + if (self->font != NULL) + return; + self->font = CTFontCreateWithFontDescriptor(self->desc, 0.0, NULL); +} + +- (CTFontSymbolicTraits)symbolicTraits +{ + return self->symbolic; +} + +- (double)weight +{ + return self->weight; +} + +- (double)width +{ + return self->width; +} + +- (CFStringRef)styleName +{ + if (!self->didStyleName) { + self->didStyleName = YES; + self->styleName = (CFStringRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontStyleNameAttribute); + // The code we use this for (guessItalicOblique() below) checks if this is NULL or not, so we're good. + } + return self->styleName; +} + +- (CFDictionaryRef)variations +{ + if (!self->didVariations) { + self->didVariations = YES; + self->variations = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontVariationsAttribute); + // This being NULL is used to determine whether a font uses variations at all, so we don't need to worry now. + } + return self->variations; +} + +- (BOOL)hasRegistrationScope +{ + if (!self->didRegistrationScope) { + CFNumberRef num; + + self->didRegistrationScope = YES; + num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); + self->hasRegistrationScope = num != NULL; + if (self->hasRegistrationScope) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->registrationScope)) == false) { + // TODO + } + CFRelease(num); + } + } + return self->hasRegistrationScope; +} + +- (CTFontManagerScope)registrationScope +{ + [self hasRegistrationScope]; + return self->registrationScope; +} + +- (CFStringRef)postScriptName +{ + if (!self->didPostScriptName) { + self->didPostScriptName = YES; + [self ensureFont]; + self->postScriptName = CTFontCopyPostScriptName(self->font); + } + return self->postScriptName; +} + +- (CFDataRef)table:(CTFontTableTag)tag +{ + [self ensureFont]; + return CTFontCopyTable(self->font, tag, kCTFontTableOptionNoOptions); +} + +- (CTFontFormat)fontFormat +{ + if (!self->didFontFormat) { + CFNumberRef num; + + self->didFontFormat = YES; + num = (CFNumberRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontFormatAttribute); + if (num == NULL) { + // TODO + } + if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->fontFormat)) == false) { + // TODO + } + CFRelease(num); + } + return self->fontFormat; +} + +// We don't need to worry if this or any of the functions that use it return NULL, because the code that uses it (libFontRegistry.dylib bug workarounds in fonttraits.m) checks for NULL. +- (CFStringRef)fontName:(CFStringRef)key +{ + [self ensureFont]; + return CTFontCopyName(self->font, key); +} + +#define FONTNAME(sel, did, var, key) \ + - (CFString)sel \ + { \ + if (!did) { \ + did = YES; \ + var = [self fontName:key]; \ + } \ + return var; \ + } +FONTNAME(preferredSubFamilyName, + self->didPreferredSubFamilyName, + self->preferredSubFamilyName, + kCTFontPreferredSubFamilyNameKey) +FONTNAME(subFamilyName, + self->didSubFamilyName, + self->subFamilyName, + kCTFontSubFamilyNameKey) +FONTNAME(fullName, + self->didFullName, + self->fullName, + kCTFontFullNameKey) +FONTNAME(preferredFamilyName, + self->didPreferredFamilyName, + self->preferredFamilyName, + kCTFontPreferredFamilyNameKey) +FONTNAME(familyName, + self->didFamilyName, + self->familyName, + kCTFontFamilyNameKey) + +- (CFArrayRef)variationAxes +{ + if (!self->didVariationAxes) { + self->didVariationAxes = YES; + [self ensureFont]; + self->variationAxes = CTFontCopyVariationAxes(self->font); + // We don't care about the return value because we call this only on fonts that we know have variations anyway. + } + return self->variationAxes; +} + +@end + struct closeness { CFIndex index; double weight; diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h new file mode 100644 index 00000000..c94ef3fd --- /dev/null +++ b/doc/export/fontstyle.h @@ -0,0 +1,56 @@ +// 3 november 2017 + +// fontstyle.m +@interface fontStyleData : NSObject { + CTFontRef font; + CTFontDescriptorRef desc; + CFDictionaryRef traits; + CTFontSymbolicTraits symbolic; + double weight; + double width; + BOOL didStyleName; + CFStringRef styleName; + BOOL didVariations; + CFDictionaryRef variations; + BOOL didRegistrationScope; + BOOL hasRegistrationScope; + CTFontManagerScope registrationScope; + BOOL didPostScriptName; + CFStringRef postScriptName; + BOOL didFontFormat; + CTFontFormat fontFormat; + BOOL didPreferredSubFamilyName; + CFStringRef preferredSubFamilyName; + BOOL didSubFamilyName; + CFStringRef subFamilyName; + BOOL didFullName; + CFStringRef fullName; + BOOL didPreferredFamilyName; + CFStringRef preferredFamilyName; + BOOL didFamilyName; + CFStringRef familyName; + BOOL didVariationAxes; + CFArrayRef variationAxes; +} +- (id)initWithFont:(CTFontRef)f; +- (id)initWithDescriptor:(CTFontDescriptorRef)d; +- (BOOL)prepare; +- (void)ensureFont; +- (CTFontSymbolicTraits)symbolicTraits; +- (double)weight; +- (double)width; +- (CFStringRef)styleName; +- (CFDictionaryRef)variations; +- (BOOL)hasRegistrationScope; +- (CTFontManagerScope)registrationScope; +- (CFStringRef)postScriptName; +- (CFDataRef)table:(CTFontTableTag)tag; +- (CTFontFormat)fontFormat; +- (CFStringRef)fontName:(CFStringRef)key; +- (CFStringRef)preferredSubFamilyName; +- (CFStringRef)subFamilyName; +- (CFStringRef)fullName; +- (CFStringRef)preferredFamilyName; +- (CFStringRef)familyName; +- (CFArrayRef)variationAxes; +@end From 51f0e3dbe5f2720510072fbcd984b0362b81735f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 18:41:13 -0400 Subject: [PATCH 325/487] Folded all CFNumber accesses into [self prepare] for error checking. This shouldn't make things *significantly* slower... --- doc/export/fontmatch.m | 48 +++++++++++++++++------------------------- doc/export/fontstyle.h | 2 -- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index 74cbe2e1..ddb7b906 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -78,6 +78,7 @@ - (BOOL)prepare { CFNumberRef num; + Boolean success; self->traits = NULL; self->symbolic = 0; @@ -87,12 +88,10 @@ self->styleName = NULL; self->didVariations = NO; self->variations = NULL; - self->didRegistrationScope = NO; self->hasRegistrationScope = NO; self->registrationScope = 0; self->didPostScriptName = NO; self->postScriptName = NULL; - self->didFontFormat = NO; self->fontFormat = 0; self->didPreferredSubFamilyName = NO; self->preferredSubFamilyName = NULL; @@ -129,6 +128,24 @@ if (CFNumberGetValue(num, kCFNumberDoubleType, &(self->width)) == false) return NO; + // do these now for the sake of error checking + num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); + self->hasRegistrationScope = num != NULL; + if (self->hasRegistrationScope) { + success = CFNumberGetValue(num, kCFNumberSInt32Type, &(self->registrationScope)); + CFRelease(num); + if (success == false) + return NO; + } + + num = (CFNumberRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontFormatAttribute); + if (num == NULL) + return NO; + success = CFNumberGetValue(num, kCFNumberSInt32Type, &(self->fontFormat)); + CFRelease(num); + if (success == false) + return NO; + return YES; } @@ -176,25 +193,11 @@ - (BOOL)hasRegistrationScope { - if (!self->didRegistrationScope) { - CFNumberRef num; - - self->didRegistrationScope = YES; - num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); - self->hasRegistrationScope = num != NULL; - if (self->hasRegistrationScope) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->registrationScope)) == false) { - // TODO - } - CFRelease(num); - } - } return self->hasRegistrationScope; } - (CTFontManagerScope)registrationScope { - [self hasRegistrationScope]; return self->registrationScope; } @@ -216,19 +219,6 @@ - (CTFontFormat)fontFormat { - if (!self->didFontFormat) { - CFNumberRef num; - - self->didFontFormat = YES; - num = (CFNumberRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontFormatAttribute); - if (num == NULL) { - // TODO - } - if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->fontFormat)) == false) { - // TODO - } - CFRelease(num); - } return self->fontFormat; } diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h index c94ef3fd..636e0992 100644 --- a/doc/export/fontstyle.h +++ b/doc/export/fontstyle.h @@ -12,12 +12,10 @@ CFStringRef styleName; BOOL didVariations; CFDictionaryRef variations; - BOOL didRegistrationScope; BOOL hasRegistrationScope; CTFontManagerScope registrationScope; BOOL didPostScriptName; CFStringRef postScriptName; - BOOL didFontFormat; CTFontFormat fontFormat; BOOL didPreferredSubFamilyName; CFStringRef preferredSubFamilyName; From 72d31285c17e325a6ee47f4c1341ce3200290183 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 18:52:15 -0400 Subject: [PATCH 326/487] Migrated fonttraits.m to use the new fontStyleData class. --- doc/export/fontmatch.m | 4 + doc/export/fonttraits.m | 172 ++++++++++------------------------------ 2 files changed, 48 insertions(+), 128 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index ddb7b906..91a3b8af 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -25,6 +25,10 @@ // because the descriptors returned by Core Text's own font // matching won't have any. +// TODO explicitly mark these as undocumented +extern const CFStringRef kCTFontPreferredSubFamilyNameKey; +extern const CFStringRef kCTFontPreferredFamilyNameKey; + @implementation fontStyleData - (id)initWithFont:(CTFontRef)f diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 0c57b9d4..919dd2a8 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -1,5 +1,6 @@ // 1 november 2017 #import "uipriv_darwin.h" +#import "fontstyle.h" // This is the part of the font style matching and normalization code // that handles fonts that use a traits dictionary. @@ -18,76 +19,30 @@ // what. We'll just convert Core Text's values into libui constants // and use those for matching. -static BOOL fontRegistered(CTFontDescriptorRef desc) +static BOOL fontRegistered(fontStyleData *d) { - CFNumberRef scope; - CTFontManagerScope val; - - scope = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); - if (scope == NULL) + if (![d hasRegistrationScope]) // header says this should be treated as the same as not registered return NO; - if (CFNumberGetValue(scope, kCFNumberSInt32Type, &val) == false) { - // TODO - CFRelease(scope); - return NO; - } - CFRelease(scope); // examination of Core Text shows this is accurate - return val != kCTFontManagerScopeNone; -} - -static BOOL getTraits(CTFontDescriptorRef desc, CTFontSymbolicTraits *symbolic, double *weight, double *width) -{ - CFDictionaryRef traits = NULL; - CFNumberRef num = NULL; - - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute); - if (traits == NULL) - return NO; - - num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (num == NULL) - goto fail; - if (CFNumberGetValue(num, kCFNumberSInt32Type, symbolic) == false) - goto fail; - - num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWeightTrait); - if (num == NULL) - goto fail; - if (CFNumberGetValue(num, kCFNumberDoubleType, weight) == false) - goto fail; - - num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWidthTrait); - if (num == NULL) - goto fail; - if (CFNumberGetValue(num, kCFNumberDoubleType, width) == false) - goto fail; - - CFRelease(traits); - return YES; - -fail: - CFRelease(traits); - return NO; + return [d registrationScope] != kCTFontManagerScopeNone; } // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextFontItalic guessItalicOblique(CTFontDescriptorRef desc) +static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) { CFStringRef styleName; BOOL isOblique; isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + styleName = [d styleName]; if (styleName != NULL) { CFRange range; range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); if (range.location != kCFNotFound) isOblique = YES; - CFRelease(styleName); } if (isOblique) return uiDrawFontItalicOblique; @@ -120,9 +75,8 @@ static const CFStringRef exceptions[] = { NULL, }; -static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) { - CTFontRef font; CFDataRef os2; uint16_t usWeightClass, usWidthClass; CFStringRef psname; @@ -132,17 +86,13 @@ static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor *hasWidth = NO; // only applies to unregistered fonts - if (fontRegistered(desc)) + if (fontRegistered(d)) return; - font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); - - os2 = CTFontCopyTable(font, kCTFontTableOS2, kCTFontTableOptionNoOptions); - if (os2 == NULL) { + os2 = [d table:kCTFontTableOS2]; + if (os2 == NULL) // no OS2 table, so no secondary values - CFRelease(font); return; - } if (CFDataGetLength(os2) > 77) { const UInt8 *b; @@ -176,49 +126,32 @@ static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor CFRelease(os2); // don't use secondary weights in the event of special predefined names - psname = CTFontCopyPostScriptName(font); + psname = [d postScriptName]; for (ex = exceptions; *ex != NULL; ex++) if (CFEqual(psname, *ex)) { *hasWeight = NO; break; } - CFRelease(psname); - - CFRelease(font); } -// TODO explicitly mark these as undocumented -extern const CFStringRef kCTFontPreferredSubFamilyNameKey; -extern const CFStringRef kCTFontPreferredFamilyNameKey; - -static const CFStringRef subfamilyKeys[] = { - kCTFontSubFamilyNameKey, - kCTFontPreferredSubFamilyNameKey, - kCTFontFullNameKey, - kCTFontPreferredFamilyNameKey, - kCTFontFamilyNameKey, - NULL, -}; - -static BOOL testTTFOTFSubfamilyNames(CTFontDescriptorRef desc, CFStringRef want) +static BOOL testTTFOTFSubfamilyName(CFStringRef name, CFStringRef want) +{ + CFRange range; + + if (name == NULL) + return NO; + range.location = 0; + range.length = CFStringGetLength(name); + return CFStringFindWithOptions(name, want, range, + (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false; +} + +static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) { - CFNumberRef num; - CTFontFormat type; CGFontRef font; CFString *key; - num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontFormatAttribute); - if (num == NULL) { - // TODO - return NO; - } - if (CFNumberGetValue(num, kCFNumberSInt32Type, &type) == false) { - // TODO - CFRelease(num); - return NO; - } - CFRelease(num); - switch (type) { + switch ([d fontFormat]) { case kCTFontFormatOpenTypePostScript: case kCTFontFormatOpenTypeTrueType: case kCTFontFormatTrueType: @@ -227,26 +160,15 @@ static BOOL testTTFOTFSubfamilyNames(CTFontDescriptorRef desc, CFStringRef want) return NO; } - font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); - for (key = subfamilyKeys; *key != NULL; key++) { - CFStringRef val; - CFRange range; - - val = CTFontCopyName(font, *key); - if (val == NULL) - continue; - range.location = 0; - range.length = CFStringGetLength(val); - if (CFStringFindWithOptions(val, want, range, - (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false) { - CFRelease(val); - CFRelease(font); - return YES; - } - CFRelease(val); - } - CFRelease(font); - return NO; + if (testTTFOTFSubfamilyName([d preferredSubFamilyName], want)) + return YES; + if (testTTFOTFSubfamilyName([d subFamilyName], want)) + return YES; + if (testTTFOTFSubfamilyName([d fullName], want)) + return YES; + if (testTTFOTFSubfamilyName([d preferredFamilyName], want)) + return YES; + return testTTFOTFSubfamilyName([d familyName], want); } // work around a bug in libFontRegistry.dylib @@ -261,27 +183,21 @@ static BOOL shouldReallyBeSemiCondensed(CTFontDescriptorRef desc) return testTTFOTFSubfamilyNames(desc, CFSTR("Semi Condensed")); } -void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) +void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) { - CTFontSymbolicTraits symbolic; - double weight; - double width; + double weight, width; BOOL hasWeight, hasWidth; - uint16_t usWeightClasss, usWidthClass; - CTFontRef font; - - if (!getTraits(desc, &symbolic, &weight, &width)) { - // TODO - goto fail; - } out->Italic = uiDrawTextItalicNormal; - if ((symbolic & kCTFontItalicTrait) != 0) - out->Italic = guessItalicOblique(desc); + if (([d symbolicTraits] & kCTFontItalicTrait) != 0) + out->Italic = guessItalicOblique(d); hasWeight = NO; hasWidth = NO; - trySecondaryOS2Values(desc, out, &hasWeight, &hasWidth); + trySecondaryOS2Values(d, out, &hasWeight, &hasWidth); + + weight = [d weight]; + width = [d width]; if (!hasWeight) // TODO this scale is a bit lopsided @@ -293,7 +209,7 @@ void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) out->Weight = uiDrawTextWeightLight; else if (weight <= -0.23) { out->Weight = uiDrawTextWeightBook; - if (shouldReallyBeThin(desc)) + if (shouldReallyBeThin(d)) out->Weight = uiDrawTextWeightThin; } else if (weight <= 0.0) out->Weight = uiDrawTextWeightNormal; @@ -314,7 +230,7 @@ void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) // TODO this scale is a bit lopsided if (width <= -0.7) { out->Stretch = uiDrawTextStretchUltraCondensed; - if (shouldReallyBeSemiCondensed(desc)) + if (shouldReallyBeSemiCondensed(d)) out->Stretch = uiDrawTextStretchSemiCondensed; } else if (width <= -0.5) out->Stretch = uiDrawTextStretchExtraCondensed; From 8333063cc0b8b45109e1606542f94c72cad57d56 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 19:52:49 -0400 Subject: [PATCH 327/487] And implemented fontvariation.m. --- doc/export/fontmatch.m | 1 + doc/export/fontstyle.h | 7 ++ doc/export/fontvariation.m | 140 ++++++++++++++++++++++++++++++++++++- 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index 91a3b8af..ae56601e 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -1,5 +1,6 @@ // 3 january 2017 #import "uipriv_darwin.h" +#import "fontstyle.h" // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h index 636e0992..a61393fb 100644 --- a/doc/export/fontstyle.h +++ b/doc/export/fontstyle.h @@ -52,3 +52,10 @@ - (CFStringRef)familyName; - (CFArrayRef)variationAxes; @end + +// fonttraits.m +extern void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out); + +// fontvariation.m +extern NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); +extern void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index 79236613..bf89220c 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -1,5 +1,6 @@ // 2 november 2017 #import "uipriv_darwin.h" +#import "fontstyle.h" // This is the part of the font style matching and normalization code // that handles fonts that use the fvar table. @@ -155,7 +156,7 @@ static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, si return fixed1616ToFixed214(val); } -fixed1616 *avarExtract(CFDataRef table, size_t index, size_t *n) +static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) { const UInt8 *b; size_t off; @@ -180,3 +181,140 @@ fixed1616 *avarExtract(CFDataRef table, size_t index, size_t *n) } return entries; } + +staatic BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed1616 *out) +{ + CFNumberRef num; + double v; + + num = (CFNumberRef) CFDictionaryGetValue(dict, key); + if (CFNumberGetValue(num, kCFNumberTypeDouble, &v) == false) + return NO; + *out = doubleToFixed1616(v); + return YES; +} + +@interface fvarAxis : NSObject { + fixed1616 min; + fixed1616 max; + fixed1616 def; + fixed1616 *avarMappings; + size_t avarCount; +} +- (id)initWithIndex:(CFIndex)i dict:(CFDictionaryRef)dict avarTable:(CFDataRef)table; +- (double)normalize:(double)v; +@end + +@implementation fvarAxis + +- (id)initWithIndex:(CFIndex)i dict:(CFDictionaryRef)dict avarTable:(CFDataRef)table +{ + self = [super init]; + if (self) { + self->avarMappings = NULL; + self->avarCount = 0; + if (!extractAxisDictValue(dict, kCTFontVariationAxisMinimumValueKey, &(self->min))) + goto fail; + if (!extractAxisDictValue(dict, kCTFontVariationAxisMaximumValueKey, &(self->max))) + goto fail; + if (!extractAxisDictValue(dict, kCTFontVariationAxisDefaultValueKey, &(self->def))) + goto fail; + if (table != NULL) + self->avarMappings = avarExtract(table, i, &(self->avarCount)); + } + return self; + +fail: + [self release]; + return nil; +} + +- (void)dealloc +{ + if (self->avarMappings != NULL) { + uiFree(self->avarMappings); + self->avarMappings = NULL; + } + [super dealloc]; +} + +- (double)normalize:(double)d +{ + fixed1616 n; + fixed214 n2; + + n = doubleToFixed1616(d); + n = fixed16161Normalize(n, self->min, self->max, self->def); + n2 = normalizedTo214(n, self->avarMappings, self->avarCount); + return fixed214ToDouble(n2); +} + +@end + +NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) +{ + CFDictionaryRef axis; + CFIndex i, n; + NSMutableDictionary *out; + + n = CFArrayGetLength(axes); + out = [NSMutableDictionary new]; + for (i = 0; i < n; i++) { + CFNumberRef key; + + axis = (CFDictionaryRef) CFArrayGetValueAtIndex(axes, i); + key = (CFNumberRef) CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey); + [out setObject:[[fvarAxis alloc] initWithIndex:i dict:axis avarTable:table] + forKey:((NSNumber *) key)]; + } + if (table != NULL) + CFRelease(table); + return out; +} + +#define fvarAxisKey(n) [NSNumber numberWithUnsignedInteger:k] + +static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, double *out) +{ + fvarAxis *axis; + CFNumberRef num; + + axis = (fvarAxis *) [axisDict objectForKey:key]; + if (axis == nil) + return NO; + num = (CFNumberRef) CFDictionaryGetValue(var, (CFNumberRef) key); + if (num == nil) + return NO; + if (CFNumberGetValue(num, kCFNumberTypeDouble, out) == false) { + // TODO + return NO; + } + *out = [axis normalize:*out]; + return YES; +} + +void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +{ + double v; + + out->Weight = uiDrawTextWeightNormal; + out->Stretch = uiDrawTextStretchNormal; + + var = [d variation]; + + if (tryAxis(axisDict, var, fvarAxisKey(fvarWeight), &v)) { + // v is now a value between -1 and 1 scaled linearly between discrete points + // we want a linear value between 0 and 1000 with 400 being normal + if (v < 0) { + v += 1; + out->Weight = (uiDrawTextWeight) (v * 400); + } else if (v > 0) + out->Weight += (uiDrawTextWeight) (v * 600); + } + + if (tryAxis(axisDict, var, fvarAxisKey(fvarWidth), &v)) { + // likewise, but with stretches, we go from 0 to 8 with 4 being directly between the two, so this is sufficient + v += 1; + out->Stretch = (uiDrawTextStretch) (v * 4); + } +} From 2276a136cb35d24313c0220c73e263da4a2d4743 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 20:33:08 -0400 Subject: [PATCH 328/487] And tied everything together. Now we move everything back and test. --- doc/export/fontmatch.m | 288 ++++++++++--------------------------- doc/export/fontstyle.h | 2 +- doc/export/fonttraits.m | 25 ---- doc/export/fontvariation.m | 2 +- 4 files changed, 79 insertions(+), 238 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index ae56601e..68e9d72b 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -279,96 +279,74 @@ FONTNAME(familyName, struct closeness { CFIndex index; - double weight; + uiDrawTextWeight weight; double italic; - double stretch; + uiDrawTextStretch stretch; double distance; }; -static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) -{ - CFNumberRef cfnum; - double val; - - cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { - // TODO - } - // Get Rule; do not release cfnum - return val; -} - -struct italicCloseness { - double normal; - double oblique; - double italic; -}; - // remember that in closeness, 0 means exact // in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match" -static const struct italicCloseness italicClosenesses[] = { - [uiDrawTextItalicNormal] = { 0, 1, 1 }, - [uiDrawTextItalicOblique] = { 1, 0, 0.5 }, - [uiDrawTextItalicItalic] = { 1, 0.5, 0 }, +static const double italicClosenessNormal[] = { 0, 1, 1 }; +static const double italicClosenessOblique[] = { 1, 0, 0.5 }; +static const double italicClosenessItalic[] = { 1, 0.5, 0 }; +static const double *italicClosenesses[] = { + [uiDrawTextItalicNormal] = italicClosenessNormal, + [uiDrawTextItalicOblique] = italicClosenessOblique, + [uiDrawTextItalicItalic] = italicClosenessItalic, }; +// Core Text doesn't seem to differentiate between Italic and Oblique. +// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess +static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) +{ + CFStringRef styleName; + BOOL isOblique; + + isOblique = NO; // default value + styleName = [d styleName]; + if (styleName != NULL) { + CFRange range; + + range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + } + if (isOblique) + return uiDrawFontItalicOblique; + return uiDrawFontItalicItalic; +} + // Italics are hard because Core Text does NOT distinguish between italic and oblique. // All Core Text provides is a slant value and the italic bit of the symbolic traits mask. // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +static void setItalic(fontStyleData *d, uiDrawFontDescriptor *out) { - const struct italicCloseness *ic = &(italicClosenesses[italic]); - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work - SInt32 s; - CFStringRef styleName; - BOOL isOblique; - - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { - // TODO - } - symbolic = (CTFontSymbolicTraits) s; - // Get Rule; do not release cfnum - if ((symbolic & kCTFontItalicTrait) == 0) - return ic->normal; - - // Okay, now we know it's either Italic or Oblique - // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); - // TODO is styleName guaranteed? - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - return ic->oblique; - return ic->italic; + out->Italic = uiDrawTextItalicNormal; + if (([d symbolicTraits] & kCTFontItalicTrait) != 0) + out->Italic = guessItalicOblique(d); } -static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +static void fillDescStyleFields(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +{ + setItalic(d, out); + if (axisDict != nil) + processFontVariation(d, axisDict, out); + else + processFontTraits(d, out); +} + +static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDescriptor *styles) { CFArrayRef matching; CFIndex i, n; struct closeness *closeness; CTFontDescriptorRef current; CTFontDescriptorRef out; + fontStyleData *d; + NSDictionary *axisDict; matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); if (matching == NULL) @@ -381,37 +359,38 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe return against; } + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matches, 0); + d = [[fontStyleData alloc] initWithDescriptor:current]; + axisDict = nil; + if ([d variations] != nil) + axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { - CFDictionaryRef traits; + uiDrawFontDescriptor fields; closeness[i].index = i; - current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { - // couldn't get traits; be safe by ranking it lowest - // LONGTERM figure out what the longest possible distances are - closeness[i].weight = 3; - closeness[i].italic = 2; - closeness[i].stretch = 3; - continue; + if (i != 0) { + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); + d = [[fontStyleData alloc] initWithDescriptor:current]; } - closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; - closeness[i].italic = italicCloseness(current, traits, targetItalic); - closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; - CFRelease(traits); + fillDescStyleFields(d, axisDict, &fields); + closeness[i].weight = fields.Weight - styles->Weight; + closeness[i].italic = italicClosenesses[styles->Italic][fields.Italic]; + closeness[i].stretch = fields.Stretch - styles->Stretch; + [d release]; } // now figure out the 3-space difference between the three and sort by that // TODO merge this loop with the previous loop? for (i = 0; i < n; i++) { - double weight, italic, stretch; + double weight, stretch; - weight = closeness[i].weight; + weight = (double) (closeness[i].weight); weight *= weight; italic = closeness[i].italic; italic *= italic; - stretch = closeness[i].stretch; + stretch = (double) (closeness[i].stretch); stretch *= stretch; closeness[i].distance = sqrt(weight + italic + stretch); } @@ -428,6 +407,8 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe CFRetain(out); // get rule // release everything + if (axisDict != nil) + [axisDict release]; uiFree(closeness); CFRelease(matching); // and release the original descriptor since we no longer need it @@ -436,60 +417,6 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe return out; } -// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights -// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these -// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) -static const double weightsToCTWeights[] = { - -1.0, // 0..99 - -0.7, // 100..199 - -0.5, // 200..299 - -0.23, // 300..399 - 0.0, // 400..499 - 0.2, // 500..599 - 0.3, // 600..699 - 0.4, // 700..799 - 0.6, // 800..899 - 0.8, // 900..999 - 1.0, // 1000 -}; - -static double weightToCTWeight(uiDrawTextWeight weight) -{ - int weightClass; - double ctclass; - double rest, weightFloor, nextFloor; - - if (weight <= 0) - return -1.0; - if (weight >= 1000) - return 1.0; - - weightClass = weight / 100; - rest = (double) weight; - weightFloor = (double) (weightClass * 100); - nextFloor = (double) ((weightClass + 1) * 100); - rest = (rest - weightFloor) / (nextFloor - weightFloor); - - ctclass = weightsToCTWeights[weightClass]; - return fma(rest, - weightsToCTWeights[weightClass + 1] - ctclass, - ctclass); -} - -// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) -static const double stretchesToCTWidths[] = { - [uiDrawTextStretchUltraCondensed] = -0.400000, - [uiDrawTextStretchExtraCondensed] = -0.300000, - [uiDrawTextStretchCondensed] = -0.200000, - [uiDrawTextStretchSemiCondensed] = -0.100000, - [uiDrawTextStretchNormal] = 0.000000, - [uiDrawTextStretchSemiExpanded] = 0.100000, - [uiDrawTextStretchExpanded] = 0.200000, - [uiDrawTextStretchExtraExpanded] = 0.300000, - // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) - [uiDrawTextStretchUltraExpanded] = 0.400000, -}; - CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) { CFMutableDictionaryRef attrs; @@ -516,10 +443,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) basedesc = CTFontDescriptorCreateWithAttributes(attrs); CFRelease(attrs); // TODO correct? - return matchTraits(basedesc, - weightToCTWeight(fd->Weight), - fd->Italic, - stretchesToCTWidths[fd->Stretch]); + return matchTraits(basedesc, fd); } // fortunately features that aren't supported are simply ignored, so we can copy them all in @@ -545,55 +469,11 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpe return new; } -// TODO deduplicate this from italicCloseness() -static uiDrawTextItalic italicFromCTItalic(CTFontDescriptorRef desc, CFDictionaryRef traits) -{ - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work - SInt32 s; - CFStringRef styleName; - BOOL isOblique; - - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { - // TODO - } - symbolic = (CTFontSymbolicTraits) s; - // Get Rule; do not release cfnum - if ((symbolic & kCTFontItalicTrait) == 0) - return uiDrawTextItalicNormal; - - // Okay, now we know it's either Italic or Oblique - // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); - // TODO is styleName guaranteed? - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - return uiDrawTextItalicOblique; - return uiDrawTextItalicItalic; -} - void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) { CFStringRef cffamily; - CFDictionaryRef traits; - double ctweight, ctstretch; - int wc; - uiDrawTextStretch stretch; + fontStyleData *d; + NSDictionary *axisDict; cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontFamilyNameAttribute); if (cffamily == NULL) { @@ -603,26 +483,12 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontTraitsAttribute); - if (traits == NULL) { - // TODO - } - ctweight = doubleAttr(traits, kCTFontWeightTrait); - uidesc->Italic = italicFromCTItalic(ctdesc, traits); - ctstretch = doubleAttr(traits, kCTFontWidthTrait); - CFRelease(traits); - - // TODO make sure this is correct - for (wc = 0; wc < 10; wc++) - if (ctweight >= weightsToCTWeights[wc]) - if (ctweight < weightsToCTWeights[wc + 1]) - break; - uidesc->Weight = ((ctweight - weightsToCTWeights[wc]) / (weightsToCTWeights[wc + 1] - weightsToCTWeights[wc])) * 100; - uidesc->Weight += wc * 100; - - // TODO is this correct? - for (stretch = uiDrawTextStretchUltraCondensed; stretch < uiDrawTextStretchUltraExpanded; stretch++) - if (ctstretch <= stretchesToCTWidths[stretch]) - break; - uidesc->Stretch = stretch; + d = [[fontStyleData alloc] initWithDescriptor:current]; + axisDict = nil; + if ([d variations] != nil) + axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + fillDescStyleFields(d, axisDict, uidesc); + if (axisDict != nil) + [axisDict release]; + [d release]; } diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h index a61393fb..a1bc66f7 100644 --- a/doc/export/fontstyle.h +++ b/doc/export/fontstyle.h @@ -1,6 +1,6 @@ // 3 november 2017 -// fontstyle.m +// fontmatch.m @interface fontStyleData : NSObject { CTFontRef font; CTFontDescriptorRef desc; diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 919dd2a8..6732b894 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -28,27 +28,6 @@ static BOOL fontRegistered(fontStyleData *d) return [d registrationScope] != kCTFontManagerScopeNone; } -// Core Text doesn't seem to differentiate between Italic and Oblique. -// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) -{ - CFStringRef styleName; - BOOL isOblique; - - isOblique = NO; // default value - styleName = [d styleName]; - if (styleName != NULL) { - CFRange range; - - range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - } - if (isOblique) - return uiDrawFontItalicOblique; - return uiDrawFontItalicItalic; -} - // Core Text does (usWidthClass / 10) - 0.5 here. // This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. // We'll just treat them as identical to 1 and 9, respectively. @@ -188,10 +167,6 @@ void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) double weight, width; BOOL hasWeight, hasWidth; - out->Italic = uiDrawTextItalicNormal; - if (([d symbolicTraits] & kCTFontItalicTrait) != 0) - out->Italic = guessItalicOblique(d); - hasWeight = NO; hasWidth = NO; trySecondaryOS2Values(d, out, &hasWeight, &hasWidth); diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index bf89220c..0e4c993b 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -300,7 +300,7 @@ void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDe out->Weight = uiDrawTextWeightNormal; out->Stretch = uiDrawTextStretchNormal; - var = [d variation]; + var = [d variations]; if (tryAxis(axisDict, var, fvarAxisKey(fvarWeight), &v)) { // v is now a value between -1 and 1 scaled linearly between discrete points From e0b584082dc0dabedac8ec51856af49249150be1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 20:59:27 -0400 Subject: [PATCH 329/487] Reintegrated everything and fixed more compiler errors. Now we have to deal with linker errors, and then with testing to see if everything worked... --- darwin/CMakeLists.txt | 2 ++ {doc/export => darwin}/fontmatch.m | 42 +++++++++++++------------- {doc/export => darwin}/fontstyle.h | 6 ++-- {doc/export => darwin}/fonttraits.m | 17 +++++------ {doc/export => darwin}/fontvariation.m | 38 ++++++++++++----------- 5 files changed, 53 insertions(+), 52 deletions(-) rename {doc/export => darwin}/fontmatch.m (94%) rename {doc/export => darwin}/fontstyle.h (95%) rename {doc/export => darwin}/fonttraits.m (94%) rename {doc/export => darwin}/fontvariation.m (89%) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index ef1d30b1..253b621c 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -21,6 +21,8 @@ list(APPEND _LIBUI_SOURCES darwin/entry.m darwin/fontbutton.m darwin/fontmatch.m + darwin/fonttraits.m + darwin/fontvariation.m darwin/form.m darwin/future.m darwin/graphemes.m diff --git a/doc/export/fontmatch.m b/darwin/fontmatch.m similarity index 94% rename from doc/export/fontmatch.m rename to darwin/fontmatch.m index 68e9d72b..6cec26b6 100644 --- a/doc/export/fontmatch.m +++ b/darwin/fontmatch.m @@ -38,7 +38,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; if (self) { self->font = f; CFRetain(self->font); - self->desc = CTFontCopyDescriptor(self->font); + self->desc = CTFontCopyFontDescriptor(self->font); if (![self prepare]) { [self release]; return nil; @@ -72,7 +72,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; REL(self->subFamilyName); REL(self->preferredSubFamilyName); REL(self->postScriptName); - REL(self->variations); + REL(self->variation); REL(self->styleName); REL(self->traits); CFRelease(self->desc); @@ -91,8 +91,8 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; self->width = 0; self->didStyleName = NO; self->styleName = NULL; - self->didVariations = NO; - self->variations = NULL; + self->didVariation = NO; + self->variation = NULL; self->hasRegistrationScope = NO; self->registrationScope = 0; self->didPostScriptName = NO; @@ -186,14 +186,14 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; return self->styleName; } -- (CFDictionaryRef)variations +- (CFDictionaryRef)variation { - if (!self->didVariations) { - self->didVariations = YES; - self->variations = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontVariationsAttribute); + if (!self->didVariation) { + self->didVariation = YES; + self->variation = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontVariationAttribute); // This being NULL is used to determine whether a font uses variations at all, so we don't need to worry now. } - return self->variations; + return self->variation; } - (BOOL)hasRegistrationScope @@ -235,7 +235,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; } #define FONTNAME(sel, did, var, key) \ - - (CFString)sel \ + - (CFStringRef)sel \ { \ if (!did) { \ did = YES; \ @@ -298,7 +298,7 @@ static const double *italicClosenesses[] = { // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) +static uiDrawTextItalic guessItalicOblique(fontStyleData *d) { CFStringRef styleName; BOOL isOblique; @@ -313,8 +313,8 @@ static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) isOblique = YES; } if (isOblique) - return uiDrawFontItalicOblique; - return uiDrawFontItalicItalic; + return uiDrawTextItalicOblique; + return uiDrawTextItalicItalic; } // Italics are hard because Core Text does NOT distinguish between italic and oblique. @@ -359,11 +359,11 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes return against; } - current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matches, 0); + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, 0); d = [[fontStyleData alloc] initWithDescriptor:current]; axisDict = nil; - if ([d variations] != nil) - axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + if ([d variation] != NULL) + axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { @@ -384,7 +384,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes // now figure out the 3-space difference between the three and sort by that // TODO merge this loop with the previous loop? for (i = 0; i < n; i++) { - double weight, stretch; + double weight, italic, stretch; weight = (double) (closeness[i].weight); weight *= weight; @@ -443,7 +443,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) basedesc = CTFontDescriptorCreateWithAttributes(attrs); CFRelease(attrs); // TODO correct? - return matchTraits(basedesc, fd); + return matchStyle(basedesc, fd); } // fortunately features that aren't supported are simply ignored, so we can copy them all in @@ -483,10 +483,10 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - d = [[fontStyleData alloc] initWithDescriptor:current]; + d = [[fontStyleData alloc] initWithDescriptor:ctdesc]; axisDict = nil; - if ([d variations] != nil) - axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + if ([d variation] != NULL) + axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); fillDescStyleFields(d, axisDict, uidesc); if (axisDict != nil) [axisDict release]; diff --git a/doc/export/fontstyle.h b/darwin/fontstyle.h similarity index 95% rename from doc/export/fontstyle.h rename to darwin/fontstyle.h index a1bc66f7..8a27380c 100644 --- a/doc/export/fontstyle.h +++ b/darwin/fontstyle.h @@ -10,8 +10,8 @@ double width; BOOL didStyleName; CFStringRef styleName; - BOOL didVariations; - CFDictionaryRef variations; + BOOL didVariation; + CFDictionaryRef variation; BOOL hasRegistrationScope; CTFontManagerScope registrationScope; BOOL didPostScriptName; @@ -38,7 +38,7 @@ - (double)weight; - (double)width; - (CFStringRef)styleName; -- (CFDictionaryRef)variations; +- (CFDictionaryRef)variation; - (BOOL)hasRegistrationScope; - (CTFontManagerScope)registrationScope; - (CFStringRef)postScriptName; diff --git a/doc/export/fonttraits.m b/darwin/fonttraits.m similarity index 94% rename from doc/export/fonttraits.m rename to darwin/fonttraits.m index 6732b894..feb37b8b 100644 --- a/doc/export/fonttraits.m +++ b/darwin/fonttraits.m @@ -31,7 +31,7 @@ static BOOL fontRegistered(fontStyleData *d) // Core Text does (usWidthClass / 10) - 0.5 here. // This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. // We'll just treat them as identical to 1 and 9, respectively. -static const uiDrawFontStretch os2WidthsToStretches[] = { +static const uiDrawTextStretch os2WidthsToStretches[] = { uiDrawTextStretchUltraCondensed, uiDrawTextStretchUltraCondensed, uiDrawTextStretchExtraCondensed, @@ -59,7 +59,7 @@ static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, B CFDataRef os2; uint16_t usWeightClass, usWidthClass; CFStringRef psname; - CFStringRef *ex; + const CFStringRef *ex; *hasWeight = NO; *hasWidth = NO; @@ -82,7 +82,7 @@ static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, B usWeightClass |= (uint16_t) (b[5]); if (usWeightClass <= 1000) { if (usWeightClass < 11) - usWeigthClass *= 100; + usWeightClass *= 100; *hasWeight = YES; } @@ -127,9 +127,6 @@ static BOOL testTTFOTFSubfamilyName(CFStringRef name, CFStringRef want) static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) { - CGFontRef font; - CFString *key; - switch ([d fontFormat]) { case kCTFontFormatOpenTypePostScript: case kCTFontFormatOpenTypeTrueType: @@ -151,15 +148,15 @@ static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeThin(CTFontDescriptorRef desc) +static BOOL shouldReallyBeThin(fontStyleData *d) { - return testTTFOTFSubfamilyNames(desc, CFSTR("W1")); + return testTTFOTFSubfamilyNames(d, CFSTR("W1")); } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeSemiCondensed(CTFontDescriptorRef desc) +static BOOL shouldReallyBeSemiCondensed(fontStyleData *d) { - return testTTFOTFSubfamilyNames(desc, CFSTR("Semi Condensed")); + return testTTFOTFSubfamilyNames(d, CFSTR("Semi Condensed")); } void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) diff --git a/doc/export/fontvariation.m b/darwin/fontvariation.m similarity index 89% rename from doc/export/fontvariation.m rename to darwin/fontvariation.m index 0e4c993b..3c6e0005 100644 --- a/doc/export/fontvariation.m +++ b/darwin/fontvariation.m @@ -88,7 +88,7 @@ static double fixed214ToDouble(fixed214 f) base = 1; break; case 2: - base = -2: + base = -2; break; case 3: base = -1; @@ -105,14 +105,14 @@ static fixed1616 fixed214ToFixed1616(fixed214 f) t = (int32_t) ((int16_t) f); t <<= 2; x = (uint32_t) t; - return (float1616) (x - 0x00000002); + return (fixed1616) (x - 0x00000002); } -static const fixed1616Negative1 = 0xFFFF0000; -static const fixed1616Zero = 0x00000000; -static const fixed1616Positive1 = 0x00010000; +static const fixed1616 fixed1616Negative1 = 0xFFFF0000; +static const fixed1616 fixed1616Zero = 0x00000000; +static const fixed1616 fixed1616Positive1 = 0x00010000; -static fixed1616 normalize1616(fixed1616 val, fixed1616 min, fixed1616 max, fixed1616 def) +static fixed1616 fixed1616Normalize(fixed1616 val, fixed1616 min, fixed1616 max, fixed1616 def) { if (val < min) val = min; @@ -133,8 +133,8 @@ static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, si val = fixed1616Positive1; if (avarCount != 0) { size_t start, end; - float1616 startFrom, endFrom; - float1616 startTo, endTo; + fixed1616 startFrom, endFrom; + fixed1616 startTo, endTo; for (end = 0; end < avarCount; end += 2) { endFrom = avarMappings[end]; @@ -175,6 +175,7 @@ static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) nEntries = nextuint16be(); *n = nEntries * 2; entries = (fixed1616 *) uiAlloc(*n * sizeof (fixed1616), "fixed1616[]"); + p = entries; for (i = 0; i < *n; i++) { *p++ = fixed214ToFixed1616((fixed214) nextuint16be()); off += 2; @@ -182,13 +183,13 @@ static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) return entries; } -staatic BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed1616 *out) +static BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed1616 *out) { CFNumberRef num; double v; num = (CFNumberRef) CFDictionaryGetValue(dict, key); - if (CFNumberGetValue(num, kCFNumberTypeDouble, &v) == false) + if (CFNumberGetValue(num, kCFNumberDoubleType, &v) == false) return NO; *out = doubleToFixed1616(v); return YES; @@ -244,7 +245,7 @@ fail: fixed214 n2; n = doubleToFixed1616(d); - n = fixed16161Normalize(n, self->min, self->max, self->def); + n = fixed1616Normalize(n, self->min, self->max, self->def); n2 = normalizedTo214(n, self->avarMappings, self->avarCount); return fixed214ToDouble(n2); } @@ -257,22 +258,22 @@ NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) CFIndex i, n; NSMutableDictionary *out; - n = CFArrayGetLength(axes); + n = CFArrayGetCount(axes); out = [NSMutableDictionary new]; for (i = 0; i < n; i++) { CFNumberRef key; axis = (CFDictionaryRef) CFArrayGetValueAtIndex(axes, i); key = (CFNumberRef) CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey); - [out setObject:[[fvarAxis alloc] initWithIndex:i dict:axis avarTable:table] + [out setObject:[[fvarAxis alloc] initWithIndex:i dict:axis avarTable:avarTable] forKey:((NSNumber *) key)]; } - if (table != NULL) - CFRelease(table); + if (avarTable != NULL) + CFRelease(avarTable); return out; } -#define fvarAxisKey(n) [NSNumber numberWithUnsignedInteger:k] +#define fvarAxisKey(n) [NSNumber numberWithUnsignedInteger:n] static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, double *out) { @@ -285,7 +286,7 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, num = (CFNumberRef) CFDictionaryGetValue(var, (CFNumberRef) key); if (num == nil) return NO; - if (CFNumberGetValue(num, kCFNumberTypeDouble, out) == false) { + if (CFNumberGetValue(num, kCFNumberDoubleType, out) == false) { // TODO return NO; } @@ -295,12 +296,13 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) { + CFDictionaryRef var; double v; out->Weight = uiDrawTextWeightNormal; out->Stretch = uiDrawTextStretchNormal; - var = [d variations]; + var = [d variation]; if (tryAxis(axisDict, var, fvarAxisKey(fvarWeight), &v)) { // v is now a value between -1 and 1 scaled linearly between discrete points From ad34745327e80c26e4361a5df120644eb07c7f28 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 21:55:43 -0400 Subject: [PATCH 330/487] =?UTF-8?q?Fixed=20loading=20of=20undocumented=20s?= =?UTF-8?q?ymbols.=20Now=20we're=20making=20progress!=20And=20what's=20mor?= =?UTF-8?q?e,=20fvar=20support=20is=20working!=20But=20not=20perfectly=20?= =?UTF-8?q?=E2=80=94=20everything=20seems=20to=20be=20hitting=20extremes..?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- darwin/CMakeLists.txt | 1 + darwin/fontmatch.m | 8 ++------ darwin/future.m | 2 -- darwin/main.m | 1 + darwin/uipriv_darwin.h | 5 +++++ darwin/undocumented.m | 31 +++++++++++++++++++++++++++++++ 6 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 darwin/undocumented.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 253b621c..d03d0f0d 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND _LIBUI_SOURCES darwin/stddialogs.m darwin/tab.m darwin/text.m + darwin/undocumented.m darwin/util.m darwin/window.m darwin/winmoveresize.m diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 6cec26b6..25476900 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -26,10 +26,6 @@ // because the descriptors returned by Core Text's own font // matching won't have any. -// TODO explicitly mark these as undocumented -extern const CFStringRef kCTFontPreferredSubFamilyNameKey; -extern const CFStringRef kCTFontPreferredFamilyNameKey; - @implementation fontStyleData - (id)initWithFont:(CTFontRef)f @@ -246,7 +242,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; FONTNAME(preferredSubFamilyName, self->didPreferredSubFamilyName, self->preferredSubFamilyName, - kCTFontPreferredSubFamilyNameKey) + UNDOC_kCTFontPreferredSubFamilyNameKey) FONTNAME(subFamilyName, self->didSubFamilyName, self->subFamilyName, @@ -258,7 +254,7 @@ FONTNAME(fullName, FONTNAME(preferredFamilyName, self->didPreferredFamilyName, self->preferredFamilyName, - kCTFontPreferredFamilyNameKey) + UNDOC_kCTFontPreferredFamilyNameKey) FONTNAME(familyName, self->didFamilyName, self->familyName, diff --git a/darwin/future.m b/darwin/future.m index a6988f83..60936f40 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -4,8 +4,6 @@ // functions and constants FROM THE FUTURE! // note: for constants, dlsym() returns the address of the constant itself, as if we had done &constantName -// TODO add weight constants here? - // added in OS X 10.10; we need 10.8 CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; diff --git a/darwin/main.m b/darwin/main.m index 60acecaf..d85000dd 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -120,6 +120,7 @@ const char *uiInit(uiInitOptions *o) initAlloc(); loadFutures(); + loadUndocumented(); // always do this so we always have an application menu appDelegate().menuManager = [[menuManager new] autorelease]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 26366520..0303c32c 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -174,3 +174,8 @@ extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; extern void loadFutures(void); extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); + +// undocumented.m +extern CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey; +extern CFStringRef UNDOC_kCTFontPreferredFamilyNameKey; +extern void loadUndocumented(void); diff --git a/darwin/undocumented.m b/darwin/undocumented.m new file mode 100644 index 00000000..0e016dd6 --- /dev/null +++ b/darwin/undocumented.m @@ -0,0 +1,31 @@ +// 3 november 2017 +#import "uipriv_darwin.h" + +// functions and constants FROM THE DEPTHS BELOW! +// note: for constants, dlsym() returns the address of the constant itself, as if we had done &constantName +// we also provide default values just in case + +// these values come from 10.12.6 +CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey = CFSTR("CTFontPreferredSubFamilyName"); +CFStringRef UNDOC_kCTFontPreferredFamilyNameKey = CFSTR("CTFontPreferredFamilyName"); + +// note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) +void loadUndocumented(void) +{ + void *handle; + CFStringRef *str; + + // dlsym() walks the dependency chain, so opening the current process should be sufficient + handle = dlopen(NULL, RTLD_LAZY); + if (handle == NULL) + return; +#define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) + GET(str, kCTFontPreferredSubFamilyNameKey); +NSLog(@"get %p", str); + if (str != NULL) + UNDOC_kCTFontPreferredSubFamilyNameKey = *str; + GET(str, kCTFontPreferredFamilyNameKey); + if (str != NULL) + UNDOC_kCTFontPreferredFamilyNameKey = *str; + dlclose(handle); +} From 6869f28718eaee3bfd4d2139e8cf13fd22c26e77 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 02:44:38 -0400 Subject: [PATCH 331/487] Fixed fixed-point math issues. fvar tables now work fine, which means Skia finally works! --- darwin/attrstr.m | 2 +- darwin/fontvariation.m | 47 +++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index cdf7334f..fd45ec25 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -55,7 +55,7 @@ void uninitUnderlineColors(void) } // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute -// TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? +// TODO opentype features are lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 3c6e0005..1eae6333 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -32,9 +32,10 @@ #define fvarWeight 0x77676874 #define fvarWidth 0x77647468 -typedef uint32_t fixed1616; -typedef uint16_t fixed214; +typedef int32_t fixed1616; +typedef int16_t fixed214; +// note that Microsoft's data type list implies that *all* fixed-point types have the same format; it only gives specific examples for the 2.14 format, which confused me because I thought 16.16 worked differently, but eh static fixed1616 doubleToFixed1616(double d) { double ipart, fpart; @@ -42,7 +43,12 @@ static fixed1616 doubleToFixed1616(double d) int16_t i16; uint32_t ret; - fpart = fabs(modf(d, &ipart)); + fpart = modf(d, &ipart); + // fpart must be unsigned; modf() gives us fpart with the same sign as f (so we have to adjust both ipart and fpart appropriately) + if (fpart < 0) { + ipart -= 1; + fpart = 1 + fpart; + } fpart *= 65536; flong = lround(fpart); i16 = (int16_t) ipart; @@ -52,14 +58,16 @@ static fixed1616 doubleToFixed1616(double d) return (fixed1616) ret; } -static double fixed1616ToDouble(fixed1616 f) +// see also https://stackoverflow.com/questions/8506317/fixed-point-unsigned-division-in-c and freetype's FT_DivFix() +// TODO figure out the specifics of freetype's more complex implementation that shifts b and juggles signs +static fixed1616 fixed1616Divide(fixed1616 a, fixed1616 b) { - int16_t base; - double frac; + uint32_t u; + int64_t a64; - base = (int16_t) ((f >> 16) & 0xFFFF); - frac = ((double) (f & 0xFFFF)) / 65536; - return ((double) base) + frac; + u = (uint32_t) a; + a64 = (int64_t) (((uint64_t) u) << 16); + return (fixed1616) (a64 / b); } static fixed214 fixed1616ToFixed214(fixed1616 f) @@ -67,7 +75,7 @@ static fixed214 fixed1616ToFixed214(fixed1616 f) uint32_t t; uint32_t topbit; - t = f + 0x00000002; + t = (uint32_t) (f + 0x00000002); topbit = t & 0x80000000; t >>= 2; if (topbit != 0) @@ -77,10 +85,12 @@ static fixed214 fixed1616ToFixed214(fixed1616 f) static double fixed214ToDouble(fixed214 f) { + uint16_t u; double base; double frac; - switch ((f >> 14) & 0x3) { + u = (uint16_t) f; + switch ((u >> 14) & 0x3) { case 0: base = 0; break; @@ -93,22 +103,20 @@ static double fixed214ToDouble(fixed214 f) case 3: base = -1; } - frac = ((double) (f & 0x3FFF)) / 16384; + frac = ((double) (u & 0x3FFF)) / 16384; return base + frac; } static fixed1616 fixed214ToFixed1616(fixed214 f) { int32_t t; - uint32_t x; t = (int32_t) ((int16_t) f); t <<= 2; - x = (uint32_t) t; - return (fixed1616) (x - 0x00000002); + return (fixed1616) (t - 0x00000002); } -static const fixed1616 fixed1616Negative1 = 0xFFFF0000; +static const fixed1616 fixed1616Negative1 = (int32_t) ((uint32_t) 0xFFFF0000); static const fixed1616 fixed1616Zero = 0x00000000; static const fixed1616 fixed1616Positive1 = 0x00010000; @@ -119,9 +127,9 @@ static fixed1616 fixed1616Normalize(fixed1616 val, fixed1616 min, fixed1616 max, if (val > max) val = max; if (val < def) - return -(def - val) / (def - min); + return fixed1616Divide(-(def - val), (def - min)); if (val > def) - return (val - def) / (max - def); + return fixed1616Divide((val - def), (max - def)); return fixed1616Zero; } @@ -148,7 +156,8 @@ static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, si start = end - 2; startFrom = avarMappings[start]; startTo = avarMappings[start + 1]; - val = (val - startFrom) / (endFrom - startFrom); + val = fixed1616Divide((val - startFrom), (endFrom - startFrom)); + // TODO find a font with an avar table and make sure this works, or if we need to use special code for this too val *= (endTo - startTo); val += startTo; } From 67a7b64f568081c6e4f43e894d7b8aeffed2824b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 02:52:12 -0400 Subject: [PATCH 332/487] More TODOs. --- darwin/fontvariation.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 1eae6333..3e22c710 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -32,6 +32,7 @@ #define fvarWeight 0x77676874 #define fvarWidth 0x77647468 +// TODO explain why these are signed typedef int32_t fixed1616; typedef int16_t fixed214; @@ -44,7 +45,7 @@ static fixed1616 doubleToFixed1616(double d) uint32_t ret; fpart = modf(d, &ipart); - // fpart must be unsigned; modf() gives us fpart with the same sign as f (so we have to adjust both ipart and fpart appropriately) + // fpart must be unsigned; modf() gives us fpart with the same sign as d (so we have to adjust both ipart and fpart appropriately) if (fpart < 0) { ipart -= 1; fpart = 1 + fpart; From 84d49cd45bfe5d6e0d126689add89d5ce347878a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 11:18:25 -0400 Subject: [PATCH 333/487] Decided to keep the minimum and maximum weights as 0 and 1000. --- ui_attrstr.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 27fd7473..a30302e0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -234,8 +234,6 @@ _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttr typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; -// TODO Minimum == 1? IIRC there is at least one font on OS X that actually has a weight of 0 -// TODO Maximum == 999? IIRC there is at least one font on OS X that actually has a weight of 1000 _UI_ENUM(uiDrawTextWeight) { uiDrawTextWeightMinimum = 0, uiDrawTextWeightThin = 100, From 243e210cbcab06602ce4ded4aea5361605e84d2f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 11:52:33 -0400 Subject: [PATCH 334/487] Resolved some darwin/drawtext.m TODOs. --- darwin/drawtext.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 43c8b4e9..1fa5920e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -167,7 +167,6 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl->u8tou16); [tl->backgroundBlocks release]; uiFree(tl->lineMetrics); - // TODO release tl->lines? CFRelease(tl->frame); CFRelease(tl->path); CFRelease(tl->framesetter); @@ -179,8 +178,11 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { backgroundBlock b; + CGAffineTransform textMatrix; CGContextSaveGState(c->c); + // save the text matrix because it's not part of the graphics state + textMatrix = CGContextGetTextMatrix(c->c); for (b in tl->backgroundBlocks) b(c, tl, x, y); @@ -190,7 +192,6 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO how is this affected by a non-identity CTM? CGContextTranslateCTM(c->c, 0, c->height); CGContextScaleCTM(c->c, 1.0, -1.0); - // TODO save the text matrix CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); // wait, that's not enough; we need to offset y values to account for our new flipping @@ -205,6 +206,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) CTFrameDraw(tl->frame, c->c); + CGContextSetTextMatrix(c->c, textMatrix); CGContextRestoreGState(c->c); } From d44c20c4a122f44b1e51dc8fae91933013893d3c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 16:48:02 -0400 Subject: [PATCH 335/487] Stashed diffs for fixing empty text layouts on OS X; the code is now utterly complicated AND my memory of what I did so far on this branch is starting to fail. --- textDarwinEmptyLayout.diff | 171 +++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 textDarwinEmptyLayout.diff diff --git a/textDarwinEmptyLayout.diff b/textDarwinEmptyLayout.diff new file mode 100644 index 00000000..bff4959f --- /dev/null +++ b/textDarwinEmptyLayout.diff @@ -0,0 +1,171 @@ +diff --git a/darwin/attrstr.m b/darwin/attrstr.m +index fd45ec25..86039fad 100644 +--- a/darwin/attrstr.m ++++ b/darwin/attrstr.m +@@ -403,8 +403,13 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) + return ps; + } + +-CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) ++static const UniChar emptyChars[] = { 0x20, 0x0 }; ++static const CFIndex emptyCharCount = 1; ++ ++CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks) + { ++ const UniChar *chars; ++ CFIndex charCount; + CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; +@@ -413,7 +418,15 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray + CFMutableAttributedStringRef mas; + struct foreachParams fep; + +- cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); ++ *isEmpty = NO; ++ chars = attrstrUTF16(p->String); ++ charCount = attrstrUTF16Len(p->String); ++ if (charCount == 0) { ++ *isEmpty = YES; ++ chars = emptyChars; ++ charCount = emptyCharCount; ++ } ++ cfstr = CFStringCreateWithCharacters(NULL, chars, charCount); + if (cfstr == NULL) { + // TODO + } +diff --git a/darwin/drawtext.m b/darwin/drawtext.m +index 1fa5920e..65912383 100644 +--- a/darwin/drawtext.m ++++ b/darwin/drawtext.m +@@ -2,13 +2,16 @@ + #import "uipriv_darwin.h" + #import "draw.h" + +-// TODO on an empty string nLines == 0 +-// we must prevent this somehow +-// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines ++// TODO in general, every function could be more robust + // TODO what happens to extents if only whitespace? ++// TODO for empty layouts: ++// - check if alignment correct compared to other OSs, or expected behavior at all ++// - double-check if uiAttributedString allows zero-length attributes; I forget if I did + + struct uiDrawTextLayout { + CFAttributedStringRef attrstr; ++ // this is needed because Core Text will give us an empty line array on a frame made with an empty string ++ BOOL isEmpty; + + // the width as passed into uiDrawTextLayout constructors + double width; +@@ -41,7 +44,7 @@ + }; + + // TODO document that lines may or may not overlap because ours do in the case of multiple combining characters +-static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) ++static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size, BOOL isEmpty) + { + uiDrawTextLayoutLineMetrics *metrics; + CFArrayRef lines; +@@ -79,6 +82,8 @@ + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; ++ if (isEmpty) ++ metrics[i].Width = 0; + metrics[i].Height = ascent + descent + leading; + + metrics[i].BaselineY = origins[i].y; +@@ -117,7 +122,7 @@ + CGRect rect; + + tl = uiNew(uiDrawTextLayout); +- tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks)); ++ tl->attrstr = attrstrToCoreFoundation(p, &(tl->isEmpty), &(tl->backgroundBlocks)); + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + tl->width = p->Width; +@@ -152,7 +157,7 @@ + + tl->lines = CTFrameGetLines(tl->frame); + tl->nLines = CFArrayGetCount(tl->lines); +- tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); ++ tl->lineMetrics = computeLineMetrics(tl->frame, tl->size, tl->isEmpty); + + // and finally copy the UTF-8/UTF-16 conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); +@@ -180,6 +185,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) + backgroundBlock b; + CGAffineTransform textMatrix; + ++ if (tl->isEmpty) ++ return; ++ + CGContextSaveGState(c->c); + // save the text matrix because it's not part of the graphics state + textMatrix = CGContextGetTextMatrix(c->c); +@@ -216,6 +224,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) + void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) + { + *width = tl->size.width; ++ if (tl->isEmpty) ++ *width = 0; + *height = tl->size.height; + } + +@@ -233,6 +243,8 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + *end = tl->u16tou8[range.location + range.length]; ++ if (tl->isEmpty) ++ *start = *end; + } + + void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) +@@ -262,14 +274,17 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p + *line = i; + + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); +- // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) +- // note: x is relative to the line origin + x -= tl->lineMetrics[*line].X; +- p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); +- if (p == kCFNotFound) { +- // TODO ++ *pos = 0; ++ if (!tl->isEmpty) { ++ // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) ++ // note: x is relative to the line origin ++ p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); ++ if (p == kCFNotFound) { ++ // TODO ++ } ++ *pos = tl->u16tou8[p]; + } +- *pos = tl->u16tou8[p]; + } + + double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +@@ -282,6 +297,9 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p + return -1; + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + range = CTLineGetStringRange(lr); ++ // TODO is the behavior of this part correct? ++ if (tl->isEmpty) ++ range.length = 0; + // note: >, not >=, because the position at end is valid! + if (pos < range.location || pos > (range.location + range.length)) + return -1; +diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h +index 0303c32c..d44ee410 100644 +--- a/darwin/uipriv_darwin.h ++++ b/darwin/uipriv_darwin.h +@@ -151,7 +151,7 @@ extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontD + extern void initUnderlineColors(void); + extern void uninitUnderlineColors(void); + typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +-extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); ++extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks); + + // aat.m + typedef void (^aatBlock)(uint16_t type, uint16_t selector); From ca5a5f1f72183edddc383cb4a5b29fb5e6de5392 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 20:47:09 -0400 Subject: [PATCH 336/487] More TODOs. I have to really think about this API and build a point-by-point test... --- common/attrlist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/attrlist.c b/common/attrlist.c index cb0833bc..53217754 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -9,6 +9,7 @@ The list is kept sorted in increasing order by start position. Whether or not th Overlapping attributes are not allowed; if an attribute is added that conflicts with an existing one, the existing one is removed. In addition, the linked list tries to reduce fragmentation: if an attribute is added that just expands another, then there will only be one entry in alist, not two. (TODO does it really?) The linked list is not a ring; alist->fist->prev == NULL and alist->last->next == NULL. +TODO verify that this disallows attributes of length zero */ struct attr { From 1f33ca14d8e99a3be6b066bf790595839b01e73a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 21:44:28 -0400 Subject: [PATCH 337/487] Fixed Windows build issues and resolved a question in libui that I need to enshrine in documentation next. --- ui_attrstr.h | 2 +- windows/attrstr.cpp | 5 +++-- windows/drawtext.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index a30302e0..e2f1f8b3 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -27,7 +27,7 @@ typedef struct uiAttributedString uiAttributedString; _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is // applied to. Use the Family field of uiAttributeSpec. - // TODO case-sensitive? + // TODO this is case-insensitive on all platforms; codify this uiAttributeFamily, // uiAttributeSize changes the size of the text it is applied to, // in typographical points. Use the Double field of diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index ca805237..c87804b5 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -61,8 +61,9 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute ostart = start; oend = end; - start = attrstrUTF8ToUTF16(s, start); - end = attrstrUTF8ToUTF16(s, end); + // TODO fix const correctness + start = attrstrUTF8ToUTF16((uiAttributedString *) s, start); + end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); range.startPosition = start; range.length = end - start; switch (spec->Type) { diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 59d64097..84a8874e 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -381,7 +381,7 @@ public: return E_UNEXPECTED; } - virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) + virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; ID2D1SolidColorBrush *brush; From e33879a283cdaace2ee0d0688dacd65f1f69098e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Nov 2017 22:52:14 -0500 Subject: [PATCH 338/487] Okay, I need to rethink these APIs. Let's start doing so. Then I should also write a testsuite for uiAttributedString. --- checklist_attrstr | 8 +++++ new_ui_attrstr.h | 92 +++++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 77 --------------------------------------- 3 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 checklist_attrstr create mode 100644 new_ui_attrstr.h diff --git a/checklist_attrstr b/checklist_attrstr new file mode 100644 index 00000000..b4b86278 --- /dev/null +++ b/checklist_attrstr @@ -0,0 +1,8 @@ += attributed strings +attribute lengths are rounded to complete unicode codepoints +zero-length attributes are elided +consecutive attributes of the same type and value are merged +overlapping attributes of different types do not split each other +overlapping attributes of the same type but different values do split +empty string is allowed +empty string cannot have attributes diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h new file mode 100644 index 00000000..083e404f --- /dev/null +++ b/new_ui_attrstr.h @@ -0,0 +1,92 @@ +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. +// +// Attributes are applied to runs of Unicode codepoints in the string. +// Zero-length runs are elided. Consecutive runs that have the same +// attribute type and value are merged. Each attribute is independent +// of each other attribute; overlapping attributes of different types +// do not split each other apart, but different values of the same +// attribute type do. +// +// The empty string can also be represented by uiAttributedString, +// but because of the no-zero-length-attribute rule, it will not have +// attributes. +// +// TODO note here about attribute ownership +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// TODO does uiAttributedString itself need this +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. +typedef struct uiAttributedString uiAttributedString; + +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +// TODO note here about attribute ownership +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with its text. +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the range [start, end). +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); + +// TODO document this +// TODO possibly copy the spec each time to ensure it doesn't get clobbered +_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/ui_attrstr.h b/ui_attrstr.h index e2f1f8b3..3f8d9f0e 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,23 +1,3 @@ -// uiAttributedString represents a string of UTF-8 text that can -// optionally be embellished with formatting attributes. libui -// provides the list of formatting attributes, which cover common -// formatting traits like boldface and color as well as advanced -// typographical features provided by OpenType like superscripts -// and small caps. These attributes can be combined in a variety of -// ways. -// -// In addition, uiAttributedString provides facilities for moving -// between grapheme clusters, which represent a character -// from the point of view of the end user. The cursor of a text editor -// is always placed on a grapheme boundary, so you can use these -// features to move the cursor left or right by one "character". -// -// uiAttributedString does not provide enough information to be able -// to draw itself onto a uiDrawContext or respond to user actions. -// In order to do that, you'll need to use a uiDrawTextLayout, which -// is built from the combination of a uiAttributedString and a set of -// layout-specific properties. -typedef struct uiAttributedString uiAttributedString; // uiAttribute specifies the types of possible attributes that can be // applied to a uiAttributedString. For every byte in the @@ -175,63 +155,6 @@ struct uiAttributeSpec { const uiOpenTypeFeatures *Features; }; -// uiAttributedStringForEachAttributeFunc is the type of the function -// invoked by uiAttributedStringForEachAttribute() for every -// attribute in s. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); - -// @role uiAttributedString constructor -// uiNewAttributedString() creates a new uiAttributedString from -// initialString. The string will be entirely unattributed. -_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); - -// @role uiAttributedString destructor -// uiFreeAttributedString() destroys the uiAttributedString s. -_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); - -// uiAttributedStringString() returns the textual content of s as a -// '\0'-terminated UTF-8 string. The returned pointer is valid until -// the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); - -// uiAttributedStringLength() returns the number of UTF-8 bytes in -// the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to the end of s. The new substring will be -// unattributed. -_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to s at the byte position specified by at. The new -// substring will be unattributed; existing attributes will be moved -// along with its text. -_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); - -// TODO add the Append and InsertAtExtendingAttributes functions -// TODO and add functions that take a string + length - -// uiAttributedStringDelete() deletes the characters and attributes of -// s in the range [start, end). -_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); - -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); - -// TODO document this -// TODO possibly copy the spec each time to ensure it doesn't get clobbered -_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); - typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; _UI_ENUM(uiDrawTextWeight) { From bad2325323310832b17288bd883d85f2c95a4f19 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Nov 2017 23:58:12 -0500 Subject: [PATCH 339/487] More attributed string API work. Of note, decided to make each type of attribute have its own field in uiAttributeSpec, to make thinking about what to do next easier (and because why not). --- checklist_attrstr | 1 + ui_attrstr.h | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index b4b86278..14dd4580 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -6,3 +6,4 @@ overlapping attributes of different types do not split each other overlapping attributes of the same type but different values do split empty string is allowed empty string cannot have attributes +font family names are case-insensitive both in attributes and in descriptors diff --git a/ui_attrstr.h b/ui_attrstr.h index 3f8d9f0e..b74f7408 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,29 +1,25 @@ // uiAttribute specifies the types of possible attributes that can be -// applied to a uiAttributedString. For every byte in the +// applied to a uiAttributedString. For every Unicode codepoint in the // uiAttributedString, at most one value of each attribute type can // be applied. // TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is - // applied to. Use the Family field of uiAttributeSpec. - // TODO this is case-insensitive on all platforms; codify this + // applied to. Font family names are case-insensitive. Use the + // Family field of uiAttributeSpec. uiAttributeFamily, // uiAttributeSize changes the size of the text it is applied to, - // in typographical points. Use the Double field of - // uiAttributeSpec. + // in typographical points. Use the Size field of uiAttributeSpec. uiAttributeSize, // uiAttributeWeight changes the weight of the text it is applied - // to. Use the Value field of uiAttributeSpec and the - // uiDrawTextWeight constants. + // to. Use the Weight field of uiAttributeSpec. uiAttributeWeight, // uiAttributeItalic changes the italicness of the text it is applied - // to. Use the Value field of uiAttributeSpec and the - // uiDrawTextItalic constants. + // to. Use the Italic field of uiAttributeSpec. uiAttributeItalic, // uiAttributeStretch changes the stretch of the text it is applied - // to. Use the Value field of uiAttributeSpec and the - // uiDrawTextStretch constants. + // to. Use the Stretch field of uiAttributeSpec. uiAttributeStretch, // uiAttributeColor changes the color of the text it is applied to. // Use the R, G, B, and A fields of uiAttributeSpec. @@ -33,16 +29,18 @@ _UI_ENUM(uiAttribute) { uiAttributeBackground, // uiAttributeUnderline changes the underline style of the text - // it is applied to. Use the Value field of uiAttributeSpec and the - // uiDrawUnderlineStyle constants. + // it is applied to. Use the UnderlineStyle field of + // uiAttributeSpec. uiAttributeUnderline, // uiAttributeUnderlineColor changes the color of any underline // on the text it is applied to, regardless of the style. Use the - // Value field of uiAttributeSpec and the uiDrawUnderlineColor - // constants (refer to its documentation for more information). + // UnderlineColor field of uiAttributeSpec, and also the R, G, B, + // and A fields if specifying uiDrawUnderlineColorCustom. // // If an underline style is applied but no underline color is - // specified, the text color is used instead. + // specified, the text color is used instead. If an underline color + // is specified without an underline style, the underline color + // attribute is ignored, but not elided. uiAttributeUnderlineColor, // uiAttributeFeatures changes the OpenType features of the @@ -146,12 +144,16 @@ typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { uiAttribute Type; const char *Family; - uintptr_t Value; - double Double; + double Size; + uiDrawTextWeight Weight; + uiDrawTextItalic Italic; + uiDrawTextStretch Stretch; double R; double G; double B; double A; + uiDrawUnderlineStyle UnderlineStyle; + uiDrawUnderlineColor UnderlineColor; const uiOpenTypeFeatures *Features; }; From 4b7ca92ce768bcca9f5a4a5bcaeabd1a434a7bdc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Dec 2017 23:47:50 -0500 Subject: [PATCH 340/487] Decided what to do about attributes. --- checklist_attrstr | 3 ++ ui_attrstr.h | 106 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 31 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index 14dd4580..8dbf504e 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -7,3 +7,6 @@ overlapping attributes of the same type but different values do split empty string is allowed empty string cannot have attributes font family names are case-insensitive both in attributes and in descriptors +attributes are unique throughout a Unicode codepoint, not just to UTF-8 bytes +define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions +does uiAttributeFamily() return a normalized string diff --git a/ui_attrstr.h b/ui_attrstr.h index b74f7408..cb562e02 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,37 +1,81 @@ +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; + +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); + +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, +}; + +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); + +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. Font family names are +// case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); + +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); + +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); + +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); -// uiAttribute specifies the types of possible attributes that can be -// applied to a uiAttributedString. For every Unicode codepoint in the -// uiAttributedString, at most one value of each attribute type can -// be applied. -// TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? -_UI_ENUM(uiAttribute) { - // uiAttributeFamily changes the font family of the text it is - // applied to. Font family names are case-insensitive. Use the - // Family field of uiAttributeSpec. - uiAttributeFamily, - // uiAttributeSize changes the size of the text it is applied to, - // in typographical points. Use the Size field of uiAttributeSpec. - uiAttributeSize, // uiAttributeWeight changes the weight of the text it is applied // to. Use the Weight field of uiAttributeSpec. - uiAttributeWeight, + uiAttributeTypeWeight, // uiAttributeItalic changes the italicness of the text it is applied // to. Use the Italic field of uiAttributeSpec. - uiAttributeItalic, + uiAttributeTypeItalic, // uiAttributeStretch changes the stretch of the text it is applied // to. Use the Stretch field of uiAttributeSpec. - uiAttributeStretch, + uiAttributeTypeStretch, // uiAttributeColor changes the color of the text it is applied to. // Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeColor, + uiAttributeTypeColor, // uiAttributeBackground changes the color of the text it is // applied to. Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeBackground, + uiAttributeTypeBackground, // uiAttributeUnderline changes the underline style of the text // it is applied to. Use the UnderlineStyle field of // uiAttributeSpec. - uiAttributeUnderline, + uiAttributeTypeUnderline, // uiAttributeUnderlineColor changes the color of any underline // on the text it is applied to, regardless of the style. Use the // UnderlineColor field of uiAttributeSpec, and also the R, G, B, @@ -41,25 +85,25 @@ _UI_ENUM(uiAttribute) { // specified, the text color is used instead. If an underline color // is specified without an underline style, the underline color // attribute is ignored, but not elided. - uiAttributeUnderlineColor, + uiAttributeTypeUnderlineColor, // uiAttributeFeatures changes the OpenType features of the // text it is applied to. Use the Features field of uiAttributeSpec. - uiAttributeFeatures, + uiAttributeTypeFeatures, }; -_UI_ENUM(uiDrawUnderlineStyle) { - uiDrawUnderlineStyleNone, - uiDrawUnderlineStyleSingle, - uiDrawUnderlineStyleDouble, - uiDrawUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +_UI_ENUM(uiUnderlineStyle) { + uiUnderlineStyleNone, + uiUnderlineStyleSingle, + uiUnderlineStyleDouble, + uiUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers }; -_UI_ENUM(uiDrawUnderlineColor) { - uiDrawUnderlineColorCustom, // also use R/G/B/A fields - uiDrawUnderlineColorSpelling, - uiDrawUnderlineColorGrammar, - uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; // uiOpenTypeFeatures represents a set of OpenType feature From 84756ab0ecea7bf82dd38877024f22ce02db28c2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Dec 2017 19:22:49 -0500 Subject: [PATCH 341/487] Finished setting up the new uiAttribute format. --- checklist_attrstr | 11 +- ui_attrstr.h | 261 +++++++++++++++++++++++++++++----------------- 2 files changed, 173 insertions(+), 99 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index 8dbf504e..89157d11 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -8,5 +8,14 @@ empty string is allowed empty string cannot have attributes font family names are case-insensitive both in attributes and in descriptors attributes are unique throughout a Unicode codepoint, not just to UTF-8 bytes -define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions +define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions and constructors does uiAttributeFamily() return a normalized string +should uiNewAttributeBackground() be renamed to uiNewAttributeBackgroundColor() and likewise for the type constant +should underline colors just ignore non-custom component arguments +should any color getter function accept a NULL pointer +what should uiAttributeUnderlineColor() do if the color type isn't Custom but the other pointers are non-NULL +should uiOpenTypeFeaturesGet() accept a NULL value pointer +what happens if uiOpenTypeFeaturesForEach() is given a NULl function pointer +should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise for the type enum +should uiNewFeaturesAttribute() accept NULL +should uiNewFamilyAttribute() accept NULL diff --git a/ui_attrstr.h b/ui_attrstr.h index cb562e02..05d541a9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -39,8 +39,9 @@ _UI_ENUM(uiAttributeType) { _UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); // uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. Font family names are -// case-insensitive. +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. _UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); // uiAttributeFamily() returns the font family stored in a. The @@ -56,56 +57,165 @@ _UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); // call this on a uiAttribute that does not hold a font size. _UI_EXTERN double uiAttributeSize(const uiAttribute *a); - // uiAttributeWeight changes the weight of the text it is applied - // to. Use the Weight field of uiAttributeSpec. - uiAttributeTypeWeight, - // uiAttributeItalic changes the italicness of the text it is applied - // to. Use the Italic field of uiAttributeSpec. - uiAttributeTypeItalic, - // uiAttributeStretch changes the stretch of the text it is applied - // to. Use the Stretch field of uiAttributeSpec. - uiAttributeTypeStretch, - // uiAttributeColor changes the color of the text it is applied to. - // Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeTypeColor, - // uiAttributeBackground changes the color of the text it is - // applied to. Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeTypeBackground, - - // uiAttributeUnderline changes the underline style of the text - // it is applied to. Use the UnderlineStyle field of - // uiAttributeSpec. - uiAttributeTypeUnderline, - // uiAttributeUnderlineColor changes the color of any underline - // on the text it is applied to, regardless of the style. Use the - // UnderlineColor field of uiAttributeSpec, and also the R, G, B, - // and A fields if specifying uiDrawUnderlineColorCustom. - // - // If an underline style is applied but no underline color is - // specified, the text color is used instead. If an underline color - // is specified without an underline style, the underline color - // attribute is ignored, but not elided. - uiAttributeTypeUnderlineColor, - - // uiAttributeFeatures changes the OpenType features of the - // text it is applied to. Use the Features field of uiAttributeSpec. - uiAttributeTypeFeatures, +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, }; -_UI_ENUM(uiUnderlineStyle) { - uiUnderlineStyleNone, - uiUnderlineStyleSingle, - uiUnderlineStyleDouble, - uiUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, }; +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. _UI_ENUM(uiUnderlineColor) { uiUnderlineColorCustom, uiUnderlineColorSpelling, uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office }; +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + // uiOpenTypeFeatures represents a set of OpenType feature // tag-value pairs, for applying OpenType features to text. // OpenType feature tags are four-character codes defined by @@ -175,68 +285,23 @@ _UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char // modify otf while uiOpenTypeFeaturesForEach() is running. _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); -// uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. -// a is defined as equal to b if and only if both have exactly the same -// tags with exactly the same values, or if both are NULL. -_UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); -typedef struct uiAttributeSpec uiAttributeSpec; +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + +// TODO CONTINUE HERE -// TODO note that pointers are copied // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit -// (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) -struct uiAttributeSpec { - uiAttribute Type; - const char *Family; - double Size; - uiDrawTextWeight Weight; - uiDrawTextItalic Italic; - uiDrawTextStretch Stretch; - double R; - double G; - double B; - double A; - uiDrawUnderlineStyle UnderlineStyle; - uiDrawUnderlineColor UnderlineColor; - const uiOpenTypeFeatures *Features; -}; typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; -_UI_ENUM(uiDrawTextWeight) { - uiDrawTextWeightMinimum = 0, - uiDrawTextWeightThin = 100, - uiDrawTextWeightUltraLight = 200, - uiDrawTextWeightLight = 300, - uiDrawTextWeightBook = 350, - uiDrawTextWeightNormal = 400, - uiDrawTextWeightMedium = 500, - uiDrawTextWeightSemiBold = 600, - uiDrawTextWeightBold = 700, - uiDrawTextWeightUltraBold = 800, - uiDrawTextWeightHeavy = 900, - uiDrawTextWeightUltraHeavy = 950, - uiDrawTextWeightMaximum = 1000, -}; - -_UI_ENUM(uiDrawTextItalic) { - uiDrawTextItalicNormal, - uiDrawTextItalicOblique, - uiDrawTextItalicItalic, -}; - -_UI_ENUM(uiDrawTextStretch) { - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchExtraCondensed, - uiDrawTextStretchCondensed, - uiDrawTextStretchSemiCondensed, - uiDrawTextStretchNormal, - uiDrawTextStretchSemiExpanded, - uiDrawTextStretchExpanded, - uiDrawTextStretchExtraExpanded, - uiDrawTextStretchUltraExpanded, -}; - struct uiDrawFontDescriptor { char *Family; double Size; From cbc78248e77a3b5eba75def70d81eb67764123ad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 13 Dec 2017 23:41:49 -0800 Subject: [PATCH 342/487] More attrstr API finalization work, --- checklist_attrstr | 1 + new_ui_attrstr.h | 344 ++++++++++++++++++++++++++++++++++++++++++++-- ui_attrstr.h | 312 ----------------------------------------- 3 files changed, 336 insertions(+), 321 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index 89157d11..e4c6bf3f 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -19,3 +19,4 @@ what happens if uiOpenTypeFeaturesForEach() is given a NULl function pointer should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise for the type enum should uiNewFeaturesAttribute() accept NULL should uiNewFamilyAttribute() accept NULL +it is an error in ForEach too diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h index 083e404f..8772f4e0 100644 --- a/new_ui_attrstr.h +++ b/new_ui_attrstr.h @@ -1,3 +1,301 @@ +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; + +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); + +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, +}; + +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); + +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); + +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); + +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); + +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); + +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, +}; + +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, +}; + +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office +}; + +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// If a font does not support a certain feature, that feature will be +// ignored. (TODO verify this on all OSs) +// +// See the OpenType specification at +// https://www.microsoft.com/typography/otspec/featuretags.htm +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; + +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); + +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + // uiAttributedString represents a string of UTF-8 text that can // optionally be embellished with formatting attributes. libui // provides the list of formatting attributes, which cover common @@ -17,7 +315,11 @@ // but because of the no-zero-length-attribute rule, it will not have // attributes. // -// TODO note here about attribute ownership +// A uiAttributedString takes ownership of all attributes given to +// it, as it may need to duplicate or delete uiAttribute objects at +// any time. By extension, when you free a uiAttributedString, +// all uiAttributes within will also be freed. Each method will +// describe its own rules in more details. // // In addition, uiAttributedString provides facilities for moving // between grapheme clusters, which represent a character @@ -37,7 +339,7 @@ typedef struct uiAttributedString uiAttributedString; // invoked by uiAttributedStringForEachAttribute() for every // attribute in s. Refer to that function's documentation for more // details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -46,7 +348,7 @@ _UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); // @role uiAttributedString destructor // uiFreeAttributedString() destroys the uiAttributedString s. -// TODO note here about attribute ownership +// It will also free all uiAttributes within. _UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); // uiAttributedStringString() returns the textual content of s as a @@ -66,16 +368,31 @@ _UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, cons // uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated // UTF-8 string str to s at the byte position specified by at. The new // substring will be unattributed; existing attributes will be moved -// along with its text. +// along with their text. _UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); // TODO add the Append and InsertAtExtendingAttributes functions // TODO and add functions that take a string + length // uiAttributedStringDelete() deletes the characters and attributes of -// s in the range [start, end). +// s in the byte range [start, end). _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + +// uiAttributedStringSetAttribute() sets a in the byte range [start, end) +// of s. Any existing attributes in that byte range of the same type are +// removed. s takes ownership of a; you should not use it after +// uiAttributedStringSetAttribute() returns. +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); + +// uiAttributedStringForEachAttribute() enumerates all the +// uiAttributes in s. It is an error to modify s in f. Within f, s still +// owns the attribute; you can neither free it nor save it for later +// use. +// TODO reword the above for consistency +_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + // TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); @@ -85,8 +402,17 @@ _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, s // TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); +// uiFontDescriptor provides a complete description of a font where +// one is needed. Currently, this means as the default font of a +// uiDrawTextLayout and as the data returned by uiFontButton. +// All the members operate like the respective uiAttributes. +typedef struct uiFontDescriptor uiFontDescriptor; -// TODO document this -// TODO possibly copy the spec each time to ensure it doesn't get clobbered -_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +struct uiDrawFontDescriptor { + // TODO const-correct this or figure out how to deal with this when getting a value + char *Family; + double Size; + uiTextWeight Weight; + uiTextItalic Italic; + uiTextStretch Stretch; +}; diff --git a/ui_attrstr.h b/ui_attrstr.h index 05d541a9..c4d8382b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,315 +1,3 @@ -// uiAttribute stores information about an attribute in a -// uiAttributedString. -// -// You do not create uiAttributes directly; instead, you create a -// uiAttribute of a given type using the specialized constructor -// functions. For every Unicode codepoint in the uiAttributedString, -// at most one value of each attribute type can be applied. -// -// uiAttributes are immutable and the uiAttributedString takes -// ownership of the uiAttribute object once assigned, copying its -// contents as necessary. -typedef struct uiAttribute uiAttribute; - -// uiFreeAttribute() frees a uiAttribute. You generally do not need to -// call this yourself, as uiAttributedString does this for you. In fact, -// it is an error to call this function on a uiAttribute that has been -// given to a uiAttributedString. You can call this, however, if you -// created a uiAttribute that you aren't going to use later. -_UI_EXTERN void uiFreeAttribute(uiAttribute *a); - -// uiAttributeType holds the possible uiAttribute types that may be -// returned by uiAttributeGetType(). Refer to the documentation for -// each type's constructor function for details on each type. -_UI_ENUM(uiAttributeType) { - uiAttributeTypeFamily, - uiAttributeTypeSize, - uiAttributeTypeWeight, - uiAttributeTypeItalic, - uiAttributeTypeStretch, - uiAttributeTypeColor, - uiAttributeTypeBackground, - uiAttributeTypeUnderline, - uiAttributeTypeUnderlineColor, - uiAttributeTypeFeatures, -}; - -// uiAttributeGetType() returns the type of a. -// TODO I don't like this name -_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); - -// uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. family is copied; you do not -// need to keep it alive after uiNewFamilyAttribute() returns. Font -// family names are case-insensitive. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); - -// uiAttributeFamily() returns the font family stored in a. The -// returned string is owned by a. It is an error to call this on a -// uiAttribute that does not hold a font family. -_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); - -// uiNewSizeAttribute() creates a new uiAttribute that changes the -// size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); - -// uiAttributeSize() returns the font size stored in a. It is an error to -// call this on a uiAttribute that does not hold a font size. -_UI_EXTERN double uiAttributeSize(const uiAttribute *a); - -// uiTextWeight represents possible text weights. These roughly -// map to the OSx2 text weight field of TrueType and OpenType -// fonts, or to CSS weight numbers. The named constants are -// nominal values; the actual values may vary by font and by OS, -// though this isn't particularly likely. Any value between -// uiTextWeightMinimum and uiDrawTextWeightMaximum, -// inclusive, is allowed. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" weights be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Black. libui does not do this, even on Windows (because the -// DirectWrite API libui uses on Windows does not do this); to -// specify Arial Black, use family Arial and weight uiTextWeightBlack. -_UI_ENUM(uiTextWeight) { - uiTextWeightMinimum = 0, - uiTextWeightThin = 100, - uiTextWeightUltraLight = 200, - uiTextWeightLight = 300, - uiTextWeightBook = 350, - uiTextWeightNormal = 400, - uiTextWeightMedium = 500, - uiTextWeightSemiBold = 600, - uiTextWeightBold = 700, - uiTextWeightUltraBold = 800, - uiTextWeightHeavy = 900, - uiTextWeightUltraHeavy = 950, - uiTextWeightMaximum = 1000, -}; - -// uiNewWeightAttribute() creates a new uiAttribute that changes the -// weight of the text it is applied to. It is an error to specify a weight -// outside the range [uiTextWeightMinimum, -// uiTextWeightMaximum]. -_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); - -// uiAttributeWeight() returns the font weight stored in a. It is an error -// to call this on a uiAttribute that does not hold a font weight. -_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); - -// uiTextItalic represents possible italic modes for a font. Italic -// represents "true" italics where the slanted glyphs have custom -// shapes, whereas oblique represents italics that are merely slanted -// versions of the normal glyphs. Most fonts usually have one or the -// other. -_UI_ENUM(uiTextItalic) { - uiTextItalicNormal, - uiTextItalicOblique, - uiTextItalicItalic, -}; - -// uiNewItalicAttribute() creates a new uiAttribute that changes the -// italic mode of the text it is applied to. It is an error to specify an -// italic mode not specified in uiTextItalic. -_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); - -// uiAttributeItalic() returns the font italic mode stored in a. It is an -// error to call this on a uiAttribute that does not hold a font italic -// mode. -_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); - -// uiTextStretch represents possible stretches (also called "widths") -// of a font. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" stretches be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Condensed. libui does not do this, even on Windows (because -// the DirectWrite API libui uses on Windows does not do this); to -// specify Arial Condensed, use family Arial and stretch -// uiTextStretchCondensed. -_UI_ENUM(uiTextStretch) { - uiTextStretchUltraCondensed, - uiTextStretchExtraCondensed, - uiTextStretchCondensed, - uiTextStretchSemiCondensed, - uiTextStretchNormal, - uiTextStretchSemiExpanded, - uiTextStretchExpanded, - uiTextStretchExtraExpanded, - uiTextStretchUltraExpanded, -}; - -// uiNewStretchAttribute() creates a new uiAttribute that changes the -// stretch of the text it is applied to. It is an error to specify a strech -// not specified in uiTextStretch. -_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); - -// uiAttributeStretch() returns the font stretch stored in a. It is an -// error to call this on a uiAttribute that does not hold a font stretch. -_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); - -// uiNewColorAttribute() creates a new uiAttribute that changes the -// color of the text it is applied to. It is an error to specify an invalid -// color. -_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); - -// uiAttributeColor() returns the text color stored in a. It is an -// error to call this on a uiAttribute that does not hold a text color. -_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); - -// uiNewBackgroundAttribute() creates a new uiAttribute that -// changes the background color of the text it is applied to. It is an -// error to specify an invalid color. -_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); - -// TODO reuse uiAttributeColor() for background colors, or make a new function... - -// uiUnderline specifies a type of underline to use on text. -_UI_ENUM(uiUnderline) { - uiUnderlineNone, - uiUnderlineSingle, - uiUnderlineDouble, - uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers -}; - -// uiNewUnderlineAttribute() creates a new uiAttribute that changes -// the type of underline on the text it is applied to. It is an error to -// specify an underline type not specified in uiUnderline. -_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); - -// uiAttributeUnderline() returns the underline type stored in a. It is -// an error to call this on a uiAttribute that does not hold an underline -// style. -_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); - -// uiUnderlineColor specifies the color of any underline on the text it -// is applied to, regardless of the type of underline. In addition to -// being able to specify a custom color, you can explicitly specify -// platform-specific colors for suggestion underlines; to use them -// correctly, pair them with uiUnderlineSuggestion (though they can -// be used on other types of underline as well). -// -// If an underline type is applied but no underline color is -// specified, the text color is used instead. If an underline color -// is specified without an underline type, the underline color -// attribute is ignored, but not removed from the uiAttributedString. -_UI_ENUM(uiUnderlineColor) { - uiUnderlineColorCustom, - uiUnderlineColorSpelling, - uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office -}; - -// uiNewUnderlineColorAttribute() creates a new uiAttribute that -// changes the color of the underline on the text it is applied to. -// It is an error to specify an underline color not specified in -// uiUnderlineColor. -// -// If the specified color type is uiUnderlineColorCustom, it is an -// error to specify an invalid color value. Otherwise, the color values -// are ignored and should be specified as zero. -_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); - -// uiAttributeUnderlineColor() returns the underline color stored in -// a. It is an error to call this on a uiAttribute that does not hold an -// underline color. -_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); - -// uiOpenTypeFeatures represents a set of OpenType feature -// tag-value pairs, for applying OpenType features to text. -// OpenType feature tags are four-character codes defined by -// OpenType that cover things from design features like small -// caps and swashes to language-specific glyph shapes and -// beyond. Each tag may only appear once in any given -// uiOpenTypeFeatures instance. Each value is a 32-bit integer, -// often used as a Boolean flag, but sometimes as an index to choose -// a glyph shape to use. -// -// If a font does not support a certain feature, that feature will be -// ignored. (TODO verify this on all OSs) -// -// See the OpenType specification at -// https://www.microsoft.com/typography/otspec/featuretags.htm -// for the complete list of available features, information on specific -// features, and how to use them. -// TODO invalid features -typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; - -// uiOpenTypeFeaturesForEachFunc is the type of the function -// invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); - -// @role uiOpenTypeFeatures constructor -// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures -// instance, with no tags yet added. -_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); - -// @role uiOpenTypeFeatures destructor -// uiFreeOpenTypeFeatures() frees otf. -_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesAdd() adds the given feature tag and value -// to otf. The feature tag is specified by a, b, c, and d. If there is -// already a value associated with the specified tag in otf, the old -// value is removed. -_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); - -// uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. If the tag is not present in otf, -// uiOpenTypeFeaturesRemove() does nothing. -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); - -// uiOpenTypeFeaturesGet() determines whether the given feature -// tag is present in otf. If it is, *value is set to the tag's value and -// nonzero is returned. Otherwise, zero is returned. -// -// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't -// changed. This is important: if a feature is not present in a -// uiOpenTypeFeatures, the feature is NOT treated as if its -// value was zero anyway. Script-specific font shaping rules and -// font-specific feature settings may use a different default value -// for a feature. You should likewise not treat a missing feature as -// having a value of zero either. Instead, a missing feature should -// be treated as having some unspecified default value. -_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); - -// uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. You cannot -// modify otf while uiOpenTypeFeaturesForEach() is running. -_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); - -// uiNewFeaturesAttribute() creates a new uiAttribute that changes -// the font family of the text it is applied to. otf is copied; you may -// free it after uiNewFeaturesAttribute() returns. -_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); - -// uiAttributeFeatures() returns the OpenType features stored in a. -// The returned uiOpenTypeFeatures object is owned by a. It is an -// error to call this on a uiAttribute that does not hold OpenType -// features. -_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); - -// TODO CONTINUE HERE - -// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - -typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; - -struct uiDrawFontDescriptor { - char *Family; - double Size; - uiDrawTextWeight Weight; - uiDrawTextItalic Italic; - uiDrawTextStretch Stretch; -}; - typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; From 686458cdb78907560ed722284f71bc8aafaa638a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 11:01:50 -0500 Subject: [PATCH 343/487] Added notes stuff. The TODO.md file will eventually wind up moving there. --- _notes/caretWidths | 1 + _notes/darwinNSAlertIcons | 8 ++++++++ _notes/misc | 2 ++ _notes/windowsHighDPI | 6 ++++++ _notes/windowsPrinting | 7 +++++++ 5 files changed, 24 insertions(+) create mode 100644 _notes/caretWidths create mode 100644 _notes/darwinNSAlertIcons create mode 100644 _notes/misc create mode 100644 _notes/windowsHighDPI create mode 100644 _notes/windowsPrinting diff --git a/_notes/caretWidths b/_notes/caretWidths new file mode 100644 index 00000000..e9eef62b --- /dev/null +++ b/_notes/caretWidths @@ -0,0 +1 @@ +UWP has this (TODO check its implementation to see if it matches ours) https://docs.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.uisettings#Windows_UI_ViewManagement_UISettings_CaretWidth diff --git a/_notes/darwinNSAlertIcons b/_notes/darwinNSAlertIcons new file mode 100644 index 00000000..5e8acfed --- /dev/null +++ b/_notes/darwinNSAlertIcons @@ -0,0 +1,8 @@ +https://www.google.com/search?q=nsalert+error+icon&client=firefox-b&tbm=isch&source=iu&ictx=1&fir=2iRctS5fJByN0M%253A%252Cw324MTzjHa1bAM%252C_&usg=__x3wpwdNN1L8VI2kHtkKAXFMtpj4%3D&sa=X&ved=0ahUKEwjJzpjN2qDZAhVjw1kKHfOHDoQQ9QEIMTAB#imgrc=2iRctS5fJByN0M: +http://0xced.blogspot.com/2009/11/clalert-nsalert-done-right.html +https://gist.github.com/0xced/228140 +http://editra.org/uploads/code/artmac.html +http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/IconServices/index.html +http://www.cocoabuilder.com/archive/cocoa/15427-iconref-to-nsimage.html +https://github.com/lukakerr/Swift-NSUserNotificationPrivate +https://stackoverflow.com/questions/32943220/the-sidebar-icon-image-name-in-osx diff --git a/_notes/misc b/_notes/misc new file mode 100644 index 00000000..72381721 --- /dev/null +++ b/_notes/misc @@ -0,0 +1,2 @@ +windows data types, "open specifications" on msdn https://msdn.microsoft.com/en-us/library/cc230321.aspx + (I usually use https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx for windows data types) diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI new file mode 100644 index 00000000..bafa40f9 --- /dev/null +++ b/_notes/windowsHighDPI @@ -0,0 +1,6 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/mt843498(v=vs.85).aspx +https://msdn.microsoft.com/library/windows/desktop/mt791579(v=vs.85).aspx +https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/ +https://channel9.msdn.com/Events/Windows/Windows-Developer-Day-Creators-Update/High-DPI-Improvements-for-Desktop-Developers +https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-bbe37ac1b0f0/per-monitor-high-dpi-awareness-wmdpichanged?forum=vcgeneral +https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363 diff --git a/_notes/windowsPrinting b/_notes/windowsPrinting new file mode 100644 index 00000000..c5d0b6c7 --- /dev/null +++ b/_notes/windowsPrinting @@ -0,0 +1,7 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/ff686805(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dn495653(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/hh448422(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd316975(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd372919(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/ee264335(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd145198(v=vs.85).aspx From a74923d57421a4ce192356475156ba97eb878841 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 11:04:02 -0500 Subject: [PATCH 344/487] Finally decided to add that httext file; gzipped to avoid git mangling it. --- examples/drawtext/httext.gz | Bin 0 -> 440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/drawtext/httext.gz diff --git a/examples/drawtext/httext.gz b/examples/drawtext/httext.gz new file mode 100644 index 0000000000000000000000000000000000000000..7fd947cf0ae1eb3e4094ecc5fa8b1687c098a5cf GIT binary patch literal 440 zcmV;p0Z0BHiwFQ3!h=`<17(uIiW5N)hF|3e4_u1;ItphZ!3ExP^3tO&GRahKZo)P% zD3pwqjGdPbCK0R&fgCo;Y&+^HFY&y^z(tc(3xj45LNakfnm!g5+6x-qQ$|C9cWU8a zgSQ{Xa8pD$F2*8O-80ej9tzbo^n9%^j9gOUqPfx;x#P2d(FQ4m@mpgxUE3}u+-)b4 zrJPL~;I-}dTprexkoj!sqoe(aN9$9T^Pra~ekGE;woC4f{=^MBHEfOb!Hd`FT28PW zuPI55HY7Wuh_~x!7mqaH2wPU9c?57OTur^LAXm<59ga$PSlj~Q31Oc66v70fu1mKe zOT06pE1Wb&N}g!nGH6)Q_ORVJzc4Sc9@$Z?mW_9h@%!`3<44Ra_l=MB*;uQ)%yX=c z-~RmkidkQR_-)YLpEX*s-UTI<-5h>AxH){s?DhJ^bVPIf{qpG-W|8B?ldD(%)l=Yj iIN9kT-P2s6Hv^L1pq`V?U0V;s9RC2*gcNnF0ssK*w%%m` literal 0 HcmV?d00001 From 17f149924682d1f47b027e29849e7466522b5d47 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 11:10:37 -0500 Subject: [PATCH 345/487] Some cleanup of the top-level directory. --- oldhaiku.tgz => _abort/oldhaiku.tgz | Bin _notes/windowsHighDPI | 3 +++ hidpinotes | 3 --- 3 files changed, 3 insertions(+), 3 deletions(-) rename oldhaiku.tgz => _abort/oldhaiku.tgz (100%) delete mode 100644 hidpinotes diff --git a/oldhaiku.tgz b/_abort/oldhaiku.tgz similarity index 100% rename from oldhaiku.tgz rename to _abort/oldhaiku.tgz diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI index bafa40f9..bfec3e7c 100644 --- a/_notes/windowsHighDPI +++ b/_notes/windowsHighDPI @@ -1,3 +1,6 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx +!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli https://msdn.microsoft.com/en-us/library/windows/desktop/mt843498(v=vs.85).aspx https://msdn.microsoft.com/library/windows/desktop/mt791579(v=vs.85).aspx https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/ diff --git a/hidpinotes b/hidpinotes deleted file mode 100644 index 74b1b021..00000000 --- a/hidpinotes +++ /dev/null @@ -1,3 +0,0 @@ -https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx -https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx -!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli From ee87a9db2383fd9333b3bb41644c3f06c9ae21be Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sat, 17 Feb 2018 18:53:32 +0100 Subject: [PATCH 346/487] Fix enter/escape crashes on Windows (#202) * Do not let slip through IDOK and IDCANCEL as menu events, fixes #55 * Add comment about IDOK and IDCANCEL --- windows/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/window.cpp b/windows/window.cpp index 9cf13a25..fbec558b 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -87,7 +87,8 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA // not a menu if (lParam != 0) break; - if (HIWORD(wParam) != 0) + // IDOK (1) and IDCANCEL (2) aren't menu events either + if (HIWORD(wParam) != 0 || LOWORD(wParam) <= IDCANCEL) break; runMenuEvent(LOWORD(wParam), uiWindow(w)); return 0; From 6c85f39584801c782275fb387ef360e86fb4159f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 12:56:47 -0500 Subject: [PATCH 347/487] Improved the comment in the previous commit. --- windows/window.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/windows/window.cpp b/windows/window.cpp index fbec558b..34baf545 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -87,7 +87,9 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA // not a menu if (lParam != 0) break; - // IDOK (1) and IDCANCEL (2) aren't menu events either + // IsDialogMessage() will also generate IDOK and IDCANCEL when pressing Enter and Escape (respectively) on some controls, like EDIT controls + // swallow those too; they'll cause runMenuEvent() to panic + // TODO fix the root cause somehow if (HIWORD(wParam) != 0 || LOWORD(wParam) <= IDCANCEL) break; runMenuEvent(LOWORD(wParam), uiWindow(w)); From dd54b3da93dd2a701078938288aafd77e163be56 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 12:59:31 -0500 Subject: [PATCH 348/487] And updated the README. Now to pull out a release. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 01275c0e..deba4dcb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ This README is being written.
## Announcements +* **17 February 2018** + * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). + * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). + * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. @@ -42,9 +46,6 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* -* ** Date: Sat, 17 Feb 2018 16:44:52 -0500 Subject: [PATCH 349/487] asdf. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index deba4dcb..794a2bf4 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This README is being written.
* **17 February 2018** * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). - * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). + * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. From a245ced3dcb13a45d1107a9e75dec26b08f6d2ad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 21:25:53 -0500 Subject: [PATCH 350/487] One last overdue README update before the package. Alpha 3.5. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 794a2bf4..d42f7e6b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ This README is being written.
* **17 February 2018** * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). + * Alpha 3.5 also includes a new control gallery example. The screenshots below have not been updated yet. * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. From ec8952a8e8b261e75938dad5b974da20004f78c0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Feb 2018 22:02:42 -0500 Subject: [PATCH 351/487] More work on new_ui_attrstr.h. Almost finished with this, actually... --- checklist_attrstr | 2 ++ new_ui_attrstr.h | 78 +++++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 34 ++------------------- 3 files changed, 82 insertions(+), 32 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index e4c6bf3f..47117def 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -20,3 +20,5 @@ should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise fo should uiNewFeaturesAttribute() accept NULL should uiNewFamilyAttribute() accept NULL it is an error in ForEach too +invalid values for uiDrawTextAlign +empty text layouts have one line diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h index 8772f4e0..f180ad66 100644 --- a/new_ui_attrstr.h +++ b/new_ui_attrstr.h @@ -416,3 +416,81 @@ struct uiDrawFontDescriptor { uiTextItalic Italic; uiTextStretch Stretch; }; + +// uiDrawTextLayout is a concrete representation of a +// uiAttributedString that can be displayed in a uiDrawContext. +// It includes information important for the drawing of a block of +// text, including the bounding box to wrap the text within, the +// alignment of lines of text within that box, areas to mark as +// being selected, and other things. +// +// Unlike uiAttributedString, the content of a uiDrawTextLayout is +// immutable once it has been created. +// +// TODO talk about OS-specific differences with text drawing that libui can't account for... +typedef struct uiDrawTextLayout uiDrawTextLayout; + +// uiDrawTextAlign specifies the alignment of lines of text in a +// uiDrawTextLayout. +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, +}; + +// uiDrawTextLayoutParams describes a uiDrawTextLayout. +// DefaultFont is used to render any text that is not attributed +// sufficiently in String. Width determines the width of the bounding +// box of the text; the height is determined automatically. +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + +// TODO const-correct this somehow +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiDrawFontDescriptor *DefaultFont; + double Width; + uiDrawTextAlign Align; +}; + +// @role uiDrawTextLayout constructor +// uiDrawNewTextLayout() creates a new uiDrawTextLayout from +// the given parameters. +// +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); + +// @role uiDrawFreeTextLayout destructor +// uiDrawFreeTextLayout() frees tl. The underlying +// uiAttributedString is not freed. +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); + +// TODO metrics functions + +// TODO number of lines visible for clipping rect, range visible for clipping rect? diff --git a/ui_attrstr.h b/ui_attrstr.h index c4d8382b..5dfc921c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,20 +1,6 @@ -typedef struct uiDrawTextLayout uiDrawTextLayout; -typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; -_UI_ENUM(uiDrawTextAlign) { - uiDrawTextAlignLeft, - uiDrawTextAlignCenter, - uiDrawTextAlignRight, -}; - -struct uiDrawTextLayoutParams { - uiAttributedString *String; - uiDrawFontDescriptor *DefaultFont; - double Width; - uiDrawTextAlign Align; -}; - // Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. // The above values are listed in vertical order, from top to bottom. // Ascent + Descent + Leading will give you the typographic bounds @@ -45,25 +31,9 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -// TODO -// - allow creating a layout out of a substring -// - allow marking compositon strings -// - allow marking selections, even after creation -// - add the following functions: -// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) -// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) -// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) -// - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -// TODO number of lines visible for clipping rect, range visible for clipping rect? -// TODO rewrite all this documentation +// TODO rewrite this documentation // uiDrawTextLayoutHitTest() returns the byte offset and line closest // to the given point. The point is relative to the top-left of the layout. From 6a737ba48eb35d166d93461563a806b775e3e779 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Feb 2018 01:46:16 -0500 Subject: [PATCH 352/487] Flipped old and new ui_attrstr.h. --- new_ui_attrstr.h | 496 ------------------------------------------ old_ui_attrstr.h | 71 ++++++ ui_attrstr.h | 549 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 558 insertions(+), 558 deletions(-) delete mode 100644 new_ui_attrstr.h create mode 100644 old_ui_attrstr.h diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h deleted file mode 100644 index f180ad66..00000000 --- a/new_ui_attrstr.h +++ /dev/null @@ -1,496 +0,0 @@ -// uiAttribute stores information about an attribute in a -// uiAttributedString. -// -// You do not create uiAttributes directly; instead, you create a -// uiAttribute of a given type using the specialized constructor -// functions. For every Unicode codepoint in the uiAttributedString, -// at most one value of each attribute type can be applied. -// -// uiAttributes are immutable and the uiAttributedString takes -// ownership of the uiAttribute object once assigned, copying its -// contents as necessary. -typedef struct uiAttribute uiAttribute; - -// uiFreeAttribute() frees a uiAttribute. You generally do not need to -// call this yourself, as uiAttributedString does this for you. In fact, -// it is an error to call this function on a uiAttribute that has been -// given to a uiAttributedString. You can call this, however, if you -// created a uiAttribute that you aren't going to use later. -_UI_EXTERN void uiFreeAttribute(uiAttribute *a); - -// uiAttributeType holds the possible uiAttribute types that may be -// returned by uiAttributeGetType(). Refer to the documentation for -// each type's constructor function for details on each type. -_UI_ENUM(uiAttributeType) { - uiAttributeTypeFamily, - uiAttributeTypeSize, - uiAttributeTypeWeight, - uiAttributeTypeItalic, - uiAttributeTypeStretch, - uiAttributeTypeColor, - uiAttributeTypeBackground, - uiAttributeTypeUnderline, - uiAttributeTypeUnderlineColor, - uiAttributeTypeFeatures, -}; - -// uiAttributeGetType() returns the type of a. -// TODO I don't like this name -_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); - -// uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. family is copied; you do not -// need to keep it alive after uiNewFamilyAttribute() returns. Font -// family names are case-insensitive. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); - -// uiAttributeFamily() returns the font family stored in a. The -// returned string is owned by a. It is an error to call this on a -// uiAttribute that does not hold a font family. -_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); - -// uiNewSizeAttribute() creates a new uiAttribute that changes the -// size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); - -// uiAttributeSize() returns the font size stored in a. It is an error to -// call this on a uiAttribute that does not hold a font size. -_UI_EXTERN double uiAttributeSize(const uiAttribute *a); - -// uiTextWeight represents possible text weights. These roughly -// map to the OSx2 text weight field of TrueType and OpenType -// fonts, or to CSS weight numbers. The named constants are -// nominal values; the actual values may vary by font and by OS, -// though this isn't particularly likely. Any value between -// uiTextWeightMinimum and uiDrawTextWeightMaximum, -// inclusive, is allowed. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" weights be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Black. libui does not do this, even on Windows (because the -// DirectWrite API libui uses on Windows does not do this); to -// specify Arial Black, use family Arial and weight uiTextWeightBlack. -_UI_ENUM(uiTextWeight) { - uiTextWeightMinimum = 0, - uiTextWeightThin = 100, - uiTextWeightUltraLight = 200, - uiTextWeightLight = 300, - uiTextWeightBook = 350, - uiTextWeightNormal = 400, - uiTextWeightMedium = 500, - uiTextWeightSemiBold = 600, - uiTextWeightBold = 700, - uiTextWeightUltraBold = 800, - uiTextWeightHeavy = 900, - uiTextWeightUltraHeavy = 950, - uiTextWeightMaximum = 1000, -}; - -// uiNewWeightAttribute() creates a new uiAttribute that changes the -// weight of the text it is applied to. It is an error to specify a weight -// outside the range [uiTextWeightMinimum, -// uiTextWeightMaximum]. -_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); - -// uiAttributeWeight() returns the font weight stored in a. It is an error -// to call this on a uiAttribute that does not hold a font weight. -_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); - -// uiTextItalic represents possible italic modes for a font. Italic -// represents "true" italics where the slanted glyphs have custom -// shapes, whereas oblique represents italics that are merely slanted -// versions of the normal glyphs. Most fonts usually have one or the -// other. -_UI_ENUM(uiTextItalic) { - uiTextItalicNormal, - uiTextItalicOblique, - uiTextItalicItalic, -}; - -// uiNewItalicAttribute() creates a new uiAttribute that changes the -// italic mode of the text it is applied to. It is an error to specify an -// italic mode not specified in uiTextItalic. -_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); - -// uiAttributeItalic() returns the font italic mode stored in a. It is an -// error to call this on a uiAttribute that does not hold a font italic -// mode. -_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); - -// uiTextStretch represents possible stretches (also called "widths") -// of a font. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" stretches be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Condensed. libui does not do this, even on Windows (because -// the DirectWrite API libui uses on Windows does not do this); to -// specify Arial Condensed, use family Arial and stretch -// uiTextStretchCondensed. -_UI_ENUM(uiTextStretch) { - uiTextStretchUltraCondensed, - uiTextStretchExtraCondensed, - uiTextStretchCondensed, - uiTextStretchSemiCondensed, - uiTextStretchNormal, - uiTextStretchSemiExpanded, - uiTextStretchExpanded, - uiTextStretchExtraExpanded, - uiTextStretchUltraExpanded, -}; - -// uiNewStretchAttribute() creates a new uiAttribute that changes the -// stretch of the text it is applied to. It is an error to specify a strech -// not specified in uiTextStretch. -_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); - -// uiAttributeStretch() returns the font stretch stored in a. It is an -// error to call this on a uiAttribute that does not hold a font stretch. -_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); - -// uiNewColorAttribute() creates a new uiAttribute that changes the -// color of the text it is applied to. It is an error to specify an invalid -// color. -_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); - -// uiAttributeColor() returns the text color stored in a. It is an -// error to call this on a uiAttribute that does not hold a text color. -_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); - -// uiNewBackgroundAttribute() creates a new uiAttribute that -// changes the background color of the text it is applied to. It is an -// error to specify an invalid color. -_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); - -// TODO reuse uiAttributeColor() for background colors, or make a new function... - -// uiUnderline specifies a type of underline to use on text. -_UI_ENUM(uiUnderline) { - uiUnderlineNone, - uiUnderlineSingle, - uiUnderlineDouble, - uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers -}; - -// uiNewUnderlineAttribute() creates a new uiAttribute that changes -// the type of underline on the text it is applied to. It is an error to -// specify an underline type not specified in uiUnderline. -_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); - -// uiAttributeUnderline() returns the underline type stored in a. It is -// an error to call this on a uiAttribute that does not hold an underline -// style. -_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); - -// uiUnderlineColor specifies the color of any underline on the text it -// is applied to, regardless of the type of underline. In addition to -// being able to specify a custom color, you can explicitly specify -// platform-specific colors for suggestion underlines; to use them -// correctly, pair them with uiUnderlineSuggestion (though they can -// be used on other types of underline as well). -// -// If an underline type is applied but no underline color is -// specified, the text color is used instead. If an underline color -// is specified without an underline type, the underline color -// attribute is ignored, but not removed from the uiAttributedString. -_UI_ENUM(uiUnderlineColor) { - uiUnderlineColorCustom, - uiUnderlineColorSpelling, - uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office -}; - -// uiNewUnderlineColorAttribute() creates a new uiAttribute that -// changes the color of the underline on the text it is applied to. -// It is an error to specify an underline color not specified in -// uiUnderlineColor. -// -// If the specified color type is uiUnderlineColorCustom, it is an -// error to specify an invalid color value. Otherwise, the color values -// are ignored and should be specified as zero. -_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); - -// uiAttributeUnderlineColor() returns the underline color stored in -// a. It is an error to call this on a uiAttribute that does not hold an -// underline color. -_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); - -// uiOpenTypeFeatures represents a set of OpenType feature -// tag-value pairs, for applying OpenType features to text. -// OpenType feature tags are four-character codes defined by -// OpenType that cover things from design features like small -// caps and swashes to language-specific glyph shapes and -// beyond. Each tag may only appear once in any given -// uiOpenTypeFeatures instance. Each value is a 32-bit integer, -// often used as a Boolean flag, but sometimes as an index to choose -// a glyph shape to use. -// -// If a font does not support a certain feature, that feature will be -// ignored. (TODO verify this on all OSs) -// -// See the OpenType specification at -// https://www.microsoft.com/typography/otspec/featuretags.htm -// for the complete list of available features, information on specific -// features, and how to use them. -// TODO invalid features -typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; - -// uiOpenTypeFeaturesForEachFunc is the type of the function -// invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); - -// @role uiOpenTypeFeatures constructor -// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures -// instance, with no tags yet added. -_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); - -// @role uiOpenTypeFeatures destructor -// uiFreeOpenTypeFeatures() frees otf. -_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesAdd() adds the given feature tag and value -// to otf. The feature tag is specified by a, b, c, and d. If there is -// already a value associated with the specified tag in otf, the old -// value is removed. -_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); - -// uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. If the tag is not present in otf, -// uiOpenTypeFeaturesRemove() does nothing. -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); - -// uiOpenTypeFeaturesGet() determines whether the given feature -// tag is present in otf. If it is, *value is set to the tag's value and -// nonzero is returned. Otherwise, zero is returned. -// -// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't -// changed. This is important: if a feature is not present in a -// uiOpenTypeFeatures, the feature is NOT treated as if its -// value was zero anyway. Script-specific font shaping rules and -// font-specific feature settings may use a different default value -// for a feature. You should likewise not treat a missing feature as -// having a value of zero either. Instead, a missing feature should -// be treated as having some unspecified default value. -_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); - -// uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. You cannot -// modify otf while uiOpenTypeFeaturesForEach() is running. -_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); - -// uiNewFeaturesAttribute() creates a new uiAttribute that changes -// the font family of the text it is applied to. otf is copied; you may -// free it after uiNewFeaturesAttribute() returns. -_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); - -// uiAttributeFeatures() returns the OpenType features stored in a. -// The returned uiOpenTypeFeatures object is owned by a. It is an -// error to call this on a uiAttribute that does not hold OpenType -// features. -_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); - -// uiAttributedString represents a string of UTF-8 text that can -// optionally be embellished with formatting attributes. libui -// provides the list of formatting attributes, which cover common -// formatting traits like boldface and color as well as advanced -// typographical features provided by OpenType like superscripts -// and small caps. These attributes can be combined in a variety of -// ways. -// -// Attributes are applied to runs of Unicode codepoints in the string. -// Zero-length runs are elided. Consecutive runs that have the same -// attribute type and value are merged. Each attribute is independent -// of each other attribute; overlapping attributes of different types -// do not split each other apart, but different values of the same -// attribute type do. -// -// The empty string can also be represented by uiAttributedString, -// but because of the no-zero-length-attribute rule, it will not have -// attributes. -// -// A uiAttributedString takes ownership of all attributes given to -// it, as it may need to duplicate or delete uiAttribute objects at -// any time. By extension, when you free a uiAttributedString, -// all uiAttributes within will also be freed. Each method will -// describe its own rules in more details. -// -// In addition, uiAttributedString provides facilities for moving -// between grapheme clusters, which represent a character -// from the point of view of the end user. The cursor of a text editor -// is always placed on a grapheme boundary, so you can use these -// features to move the cursor left or right by one "character". -// TODO does uiAttributedString itself need this -// -// uiAttributedString does not provide enough information to be able -// to draw itself onto a uiDrawContext or respond to user actions. -// In order to do that, you'll need to use a uiDrawTextLayout, which -// is built from the combination of a uiAttributedString and a set of -// layout-specific properties. -typedef struct uiAttributedString uiAttributedString; - -// uiAttributedStringForEachAttributeFunc is the type of the function -// invoked by uiAttributedStringForEachAttribute() for every -// attribute in s. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); - -// @role uiAttributedString constructor -// uiNewAttributedString() creates a new uiAttributedString from -// initialString. The string will be entirely unattributed. -_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); - -// @role uiAttributedString destructor -// uiFreeAttributedString() destroys the uiAttributedString s. -// It will also free all uiAttributes within. -_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); - -// uiAttributedStringString() returns the textual content of s as a -// '\0'-terminated UTF-8 string. The returned pointer is valid until -// the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); - -// uiAttributedStringLength() returns the number of UTF-8 bytes in -// the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to the end of s. The new substring will be -// unattributed. -_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); - -// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated -// UTF-8 string str to s at the byte position specified by at. The new -// substring will be unattributed; existing attributes will be moved -// along with their text. -_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); - -// TODO add the Append and InsertAtExtendingAttributes functions -// TODO and add functions that take a string + length - -// uiAttributedStringDelete() deletes the characters and attributes of -// s in the byte range [start, end). -_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); - -// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - -// uiAttributedStringSetAttribute() sets a in the byte range [start, end) -// of s. Any existing attributes in that byte range of the same type are -// removed. s takes ownership of a; you should not use it after -// uiAttributedStringSetAttribute() returns. -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); - -// uiAttributedStringForEachAttribute() enumerates all the -// uiAttributes in s. It is an error to modify s in f. Within f, s still -// owns the attribute; you can neither free it nor save it for later -// use. -// TODO reword the above for consistency -_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); - -// uiFontDescriptor provides a complete description of a font where -// one is needed. Currently, this means as the default font of a -// uiDrawTextLayout and as the data returned by uiFontButton. -// All the members operate like the respective uiAttributes. -typedef struct uiFontDescriptor uiFontDescriptor; - -struct uiDrawFontDescriptor { - // TODO const-correct this or figure out how to deal with this when getting a value - char *Family; - double Size; - uiTextWeight Weight; - uiTextItalic Italic; - uiTextStretch Stretch; -}; - -// uiDrawTextLayout is a concrete representation of a -// uiAttributedString that can be displayed in a uiDrawContext. -// It includes information important for the drawing of a block of -// text, including the bounding box to wrap the text within, the -// alignment of lines of text within that box, areas to mark as -// being selected, and other things. -// -// Unlike uiAttributedString, the content of a uiDrawTextLayout is -// immutable once it has been created. -// -// TODO talk about OS-specific differences with text drawing that libui can't account for... -typedef struct uiDrawTextLayout uiDrawTextLayout; - -// uiDrawTextAlign specifies the alignment of lines of text in a -// uiDrawTextLayout. -_UI_ENUM(uiDrawTextAlign) { - uiDrawTextAlignLeft, - uiDrawTextAlignCenter, - uiDrawTextAlignRight, -}; - -// uiDrawTextLayoutParams describes a uiDrawTextLayout. -// DefaultFont is used to render any text that is not attributed -// sufficiently in String. Width determines the width of the bounding -// box of the text; the height is determined automatically. -typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; - -// TODO const-correct this somehow -struct uiDrawTextLayoutParams { - uiAttributedString *String; - uiDrawFontDescriptor *DefaultFont; - double Width; - uiDrawTextAlign Align; -}; - -// @role uiDrawTextLayout constructor -// uiDrawNewTextLayout() creates a new uiDrawTextLayout from -// the given parameters. -// -// TODO -// - allow creating a layout out of a substring -// - allow marking compositon strings -// - allow marking selections, even after creation -// - add the following functions: -// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) -// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) -// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) -// - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); - -// @role uiDrawFreeTextLayout destructor -// uiDrawFreeTextLayout() frees tl. The underlying -// uiAttributedString is not freed. -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); - -// uiDrawText() draws tl in c with the top-left point of tl at (x, y). -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); - -// uiDrawTextLayoutExtents() returns the width and height of tl -// in width and height. The returned width may be smaller than -// the width passed into uiDrawNewTextLayout() depending on -// how the text in tl is wrapped. Therefore, you can use this -// function to get the actual size of the text layout. -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); - -// uiDrawTextLayoutNumLines() returns the number of lines in tl. -// This number will always be greater than or equal to 1; a text -// layout with no text only has one line. -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); - -// uiDrawTextLayoutLineByteRange() returns the byte indices of the -// text that falls into the given line of tl as [start, end). -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); - -// TODO metrics functions - -// TODO number of lines visible for clipping rect, range visible for clipping rect? diff --git a/old_ui_attrstr.h b/old_ui_attrstr.h new file mode 100644 index 00000000..5dfc921c --- /dev/null +++ b/old_ui_attrstr.h @@ -0,0 +1,71 @@ + +typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; + +// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. +// The above values are listed in vertical order, from top to bottom. +// Ascent + Descent + Leading will give you the typographic bounds +// of the text. BaselineY is the boundary between Ascent and Descent. +// X, Y, and BaselineY are all in the layout's coordinate system, so the +// start point of the baseline will be at (X, BaselineY). All values are +// nonnegative. +struct uiDrawTextLayoutLineMetrics { + // This describes the overall bounding box of the line. + double X; + double Y; + double Width; + double Height; + + // This describes the typographic bounds of the line. + double BaselineY; + double Ascent; + double Descent; + double Leading; + + // This describes any additional whitespace. + // TODO come up with better names for these. + double ParagraphSpacingBefore; + double LineHeightSpace; + double LineSpacing; + double ParagraphSpacing; + + // TODO trailing whitespace? +}; + +_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); + +// TODO rewrite this documentation + +// uiDrawTextLayoutHitTest() returns the byte offset and line closest +// to the given point. The point is relative to the top-left of the layout. +// If the point is outside the layout itself, the closest point is chosen; +// this allows the function to be used for cursor positioning with the +// mouse. Do keep the returned line in mind if used in this way; the +// user might click on the end of a line, at which point the cursor +// might be at the trailing edge of the last grapheme on the line +// (subject to the operating system's APIs). +_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); + +// uiDrawTextLayoutByteLocationInLine() returns the point offset +// into the given line that the given byte position stands. This is +// relative to the line's X position (as returned by +// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to +// the top-left of the layout. This function can be used for cursor +// positioning: if start and end are the start and end of the line +// (as returned by uiDrawTextLayoutLineByteRange()), you will get +// the correct offset, even if pos is at the end of the line. If pos is not +// in the range [start, end], a negative value will be returned, +// indicating you need to move the cursor to another line. +// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text +_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); + +_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); +// TODO allow blinking +// TODO allow secondary carets + +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); +// TOOD SetFont, mechanics +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +_UI_EXTERN uiFontButton *uiNewFontButton(void); diff --git a/ui_attrstr.h b/ui_attrstr.h index 5dfc921c..f180ad66 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,71 +1,496 @@ +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; -typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); -// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. -// The above values are listed in vertical order, from top to bottom. -// Ascent + Descent + Leading will give you the typographic bounds -// of the text. BaselineY is the boundary between Ascent and Descent. -// X, Y, and BaselineY are all in the layout's coordinate system, so the -// start point of the baseline will be at (X, BaselineY). All values are -// nonnegative. -struct uiDrawTextLayoutLineMetrics { - // This describes the overall bounding box of the line. - double X; - double Y; - double Width; - double Height; - - // This describes the typographic bounds of the line. - double BaselineY; - double Ascent; - double Descent; - double Leading; - - // This describes any additional whitespace. - // TODO come up with better names for these. - double ParagraphSpacingBefore; - double LineHeightSpace; - double LineSpacing; - double ParagraphSpacing; - - // TODO trailing whitespace? +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, }; -_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); -// TODO rewrite this documentation +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); -// uiDrawTextLayoutHitTest() returns the byte offset and line closest -// to the given point. The point is relative to the top-left of the layout. -// If the point is outside the layout itself, the closest point is chosen; -// this allows the function to be used for cursor positioning with the -// mouse. Do keep the returned line in mind if used in this way; the -// user might click on the end of a line, at which point the cursor -// might be at the trailing edge of the last grapheme on the line -// (subject to the operating system's APIs). -_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); -// uiDrawTextLayoutByteLocationInLine() returns the point offset -// into the given line that the given byte position stands. This is -// relative to the line's X position (as returned by -// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to -// the top-left of the layout. This function can be used for cursor -// positioning: if start and end are the start and end of the line -// (as returned by uiDrawTextLayoutLineByteRange()), you will get -// the correct offset, even if pos is at the end of the line. If pos is not -// in the range [start, end], a negative value will be returned, -// indicating you need to move the cursor to another line. -// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text -_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); -_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); -// TODO allow blinking -// TODO allow secondary carets +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? -_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); -// TOOD SetFont, mechanics -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -_UI_EXTERN uiFontButton *uiNewFontButton(void); +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, +}; + +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, +}; + +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office +}; + +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// If a font does not support a certain feature, that feature will be +// ignored. (TODO verify this on all OSs) +// +// See the OpenType specification at +// https://www.microsoft.com/typography/otspec/featuretags.htm +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; + +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); + +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. +// +// Attributes are applied to runs of Unicode codepoints in the string. +// Zero-length runs are elided. Consecutive runs that have the same +// attribute type and value are merged. Each attribute is independent +// of each other attribute; overlapping attributes of different types +// do not split each other apart, but different values of the same +// attribute type do. +// +// The empty string can also be represented by uiAttributedString, +// but because of the no-zero-length-attribute rule, it will not have +// attributes. +// +// A uiAttributedString takes ownership of all attributes given to +// it, as it may need to duplicate or delete uiAttribute objects at +// any time. By extension, when you free a uiAttributedString, +// all uiAttributes within will also be freed. Each method will +// describe its own rules in more details. +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// TODO does uiAttributedString itself need this +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. +typedef struct uiAttributedString uiAttributedString; + +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +// It will also free all uiAttributes within. +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with their text. +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the byte range [start, end). +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + +// uiAttributedStringSetAttribute() sets a in the byte range [start, end) +// of s. Any existing attributes in that byte range of the same type are +// removed. s takes ownership of a; you should not use it after +// uiAttributedStringSetAttribute() returns. +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); + +// uiAttributedStringForEachAttribute() enumerates all the +// uiAttributes in s. It is an error to modify s in f. Within f, s still +// owns the attribute; you can neither free it nor save it for later +// use. +// TODO reword the above for consistency +_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +// uiFontDescriptor provides a complete description of a font where +// one is needed. Currently, this means as the default font of a +// uiDrawTextLayout and as the data returned by uiFontButton. +// All the members operate like the respective uiAttributes. +typedef struct uiFontDescriptor uiFontDescriptor; + +struct uiDrawFontDescriptor { + // TODO const-correct this or figure out how to deal with this when getting a value + char *Family; + double Size; + uiTextWeight Weight; + uiTextItalic Italic; + uiTextStretch Stretch; +}; + +// uiDrawTextLayout is a concrete representation of a +// uiAttributedString that can be displayed in a uiDrawContext. +// It includes information important for the drawing of a block of +// text, including the bounding box to wrap the text within, the +// alignment of lines of text within that box, areas to mark as +// being selected, and other things. +// +// Unlike uiAttributedString, the content of a uiDrawTextLayout is +// immutable once it has been created. +// +// TODO talk about OS-specific differences with text drawing that libui can't account for... +typedef struct uiDrawTextLayout uiDrawTextLayout; + +// uiDrawTextAlign specifies the alignment of lines of text in a +// uiDrawTextLayout. +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, +}; + +// uiDrawTextLayoutParams describes a uiDrawTextLayout. +// DefaultFont is used to render any text that is not attributed +// sufficiently in String. Width determines the width of the bounding +// box of the text; the height is determined automatically. +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + +// TODO const-correct this somehow +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiDrawFontDescriptor *DefaultFont; + double Width; + uiDrawTextAlign Align; +}; + +// @role uiDrawTextLayout constructor +// uiDrawNewTextLayout() creates a new uiDrawTextLayout from +// the given parameters. +// +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); + +// @role uiDrawFreeTextLayout destructor +// uiDrawFreeTextLayout() frees tl. The underlying +// uiAttributedString is not freed. +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); + +// TODO metrics functions + +// TODO number of lines visible for clipping rect, range visible for clipping rect? From eeb7717d88d3bd5a62946d30fb3c7c38f4a6c720 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Feb 2018 01:52:38 -0500 Subject: [PATCH 353/487] Moved old attributed-string stuff out of the way for now. --- common/{attrlist.c => OLD_attrlist.c} | 0 common/{attrstr.c => OLD_attrstr.c} | 0 common/{drawtext.c => OLD_drawtext.c} | 0 common/OLD_uipriv_attrstr.h | 43 +++++++++++++++++++++++++++ common/uipriv.h | 43 --------------------------- 5 files changed, 43 insertions(+), 43 deletions(-) rename common/{attrlist.c => OLD_attrlist.c} (100%) rename common/{attrstr.c => OLD_attrstr.c} (100%) rename common/{drawtext.c => OLD_drawtext.c} (100%) create mode 100644 common/OLD_uipriv_attrstr.h diff --git a/common/attrlist.c b/common/OLD_attrlist.c similarity index 100% rename from common/attrlist.c rename to common/OLD_attrlist.c diff --git a/common/attrstr.c b/common/OLD_attrstr.c similarity index 100% rename from common/attrstr.c rename to common/OLD_attrstr.c diff --git a/common/drawtext.c b/common/OLD_drawtext.c similarity index 100% rename from common/drawtext.c rename to common/OLD_drawtext.c diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h new file mode 100644 index 00000000..6a1a63ce --- /dev/null +++ b/common/OLD_uipriv_attrstr.h @@ -0,0 +1,43 @@ + +// for attrstr.c +struct graphemes { + size_t len; + size_t *pointsToGraphemes; + size_t *graphemesToPoints; +}; +extern int graphemesTakesUTF16(void); +extern struct graphemes *graphemes(void *s, size_t len); + +// TODO split these into a separate header file? + +// attrstr.c +extern const uint16_t *attrstrUTF16(uiAttributedString *s); +extern size_t attrstrUTF16Len(uiAttributedString *s); +extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); +extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); + +// attrlist.c +struct attrlist; +extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); +extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); +extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); +extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); +extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); +extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); +extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +// TODO move these to the top like everythng else +extern struct attrlist *attrlistNew(void); +extern void attrlistFree(struct attrlist *alist); + +// drawtext.c +struct caretDrawParams { + double r; + double g; + double b; + double a; + double xoff; + double width; +}; +extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); +extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); diff --git a/common/uipriv.h b/common/uipriv.h index 553073f5..3cd23605 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -58,49 +58,6 @@ extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); -// for attrstr.c -struct graphemes { - size_t len; - size_t *pointsToGraphemes; - size_t *graphemesToPoints; -}; -extern int graphemesTakesUTF16(void); -extern struct graphemes *graphemes(void *s, size_t len); - -// TODO split these into a separate header file? - -// attrstr.c -extern const uint16_t *attrstrUTF16(uiAttributedString *s); -extern size_t attrstrUTF16Len(uiAttributedString *s); -extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); -extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); -extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); - -// attrlist.c -struct attrlist; -extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); -extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); -extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); -extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); -extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); -extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); -// TODO move these to the top like everythng else -extern struct attrlist *attrlistNew(void); -extern void attrlistFree(struct attrlist *alist); - -// drawtext.c -struct caretDrawParams { - double r; - double g; - double b; - double a; - double xoff; - double width; -}; -extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); -extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); - #ifdef __cplusplus } #endif From 93bf0d403e2b4c378b6f9b110933944e5bd9b2f9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Feb 2018 18:37:50 -0500 Subject: [PATCH 354/487] Wrote the new uiAttribute code in attribute.c. --- TODO.md | 3 + common/attribute.c | 235 +++++++++++++++++++++++++++++++++++++++++++++ common/attrstr.h | 4 + ui_attrstr.h | 2 +- 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 common/attribute.c create mode 100644 common/attrstr.h diff --git a/TODO.md b/TODO.md index 6389f1f8..528a658d 100644 --- a/TODO.md +++ b/TODO.md @@ -257,3 +257,6 @@ mac os x accessibility uiEntry disabling bugs http://www.cocoabuilder.com/archive/cocoa/215525-nstextfield-bug-can-be.html uiMultilineEntry disabling https://developer.apple.com/library/content/qa/qa1461/_index.html + +more TODOs: +- make no guarantee about buildability of feature branches diff --git a/common/attribute.c b/common/attribute.c new file mode 100644 index 00000000..49dc860d --- /dev/null +++ b/common/attribute.c @@ -0,0 +1,235 @@ +// 19 february 2018 +#include "uipriv.h" +#include "attrstr.h" + +struct uiAttribute { + uiAttributeType type; + union { + const char *family; + double size; + uiTextWeight weight; + uiTextItalic italic; + uiTextStretch stretch; + struct { + double r; + double g; + double b; + double a; + // put this here so we can reuse this structure + uiUnderlineColor underlineColor; + } color; + uiUnderline underline; + uiOpenTypeFeatures *features; + } u; +}; + +static uiAttribute *newAttribute(uiAttributeType type) +{ + uiAttribute *a; + + a = uiprivNew(uiAttribute); + a->type = type; + return a; +} + +void uiFreeAttribute(uiAttribute *a) +[ + switch (a->type) { + case uiAttributeTypeFamily: + uiprivFree(a->u.family); + break; + case uiAttributeTypeFeatures: + uiFreeOpenTypeFeatures(a->u.features); + break; + } + uiprivFree(a); +} + +uiAttributeType uiAttributeGetType(const uiAttribute *a) +{ + return a->type; +} + +uiAttribute *uiNewFamilyAttribute(const char *family) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeFamily); + a->u.family = uiprivStrdup(family); + return a; +} + +const char *uiAttributeFamily(const uiAttribute *a) +{ + return a->u.family; +} + +uiAttribute *uiNewSizeAttribute(double size) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeSize); + a->u.size = size; + return a; +} + +double uiAttributeSize(const uiAttribute *a) +{ + return a->u.size; +} + +uiAttribute *uiNewWeightAttribute(uiTextWeight weight) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeWeight); + a->u.weight = weight; + return a; +} + +uiTextWeight uiAttributeWeight(const uiAttribute *a) +{ + return a->u.weight; +} + +uiAttribute *uiNewItalicAttribute(uiTextItalic italic) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeItalic); + a->u.italic = italic; + return a; +} + +uiTextItalic uiAttributeItalic(const uiAttribute *a) +{ + return a->u.italic; +} + +uiAttribute *uiNewStretchAttribute(uiTextStretch stretch) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeStretch); + a->u.stretch = stretch; + return a; +} + +uiTextStretch uiAttributeStretch(const uiAttribute *a) +{ + return a->u.stretch; +} + +uiAttribute *uiNewColorAttribute(double r, double g, double b, double a) +{ + uiAttribute *at; + + at = newAttribute(uiAttributeTypeColor); + at->u.color.r = r; + at->u.color.g = g; + at->u.color.b = b; + at->u.color.a = a; + return at; +} + +void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha) +{ + *r = a->u.color.r; + *g = a->u.color.g; + *b = a->u.color.b; + *alpha = a->u.color.a; +} + +uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a) +{ + uiAttribute *at; + + at = newAttribute(uiAttributeTypeBackgroundColor); + at->u.color.r = r; + at->u.color.g = g; + at->u.color.b = b; + at->u.color.a = a; + return at; +} + +uiAttribute *uiNewUnderlineAttribute(uiUnderline u) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeUnderline); + a->u.underline = u; + return a; +} + +uiUnderline uiAttributeUnderline(const uiAttribute *a) +{ + return a->u.underline; +} + +uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a) +{ + uiAttribute *at; + + at = uiNewColorAttribute(r, g, b, a); + at->type = uiAttributeTypeUnderlineColor; + at->u.color.underlineColor = u; + return at; +} + +void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha) +{ + *u = a->u.color.underlineColor; + uiAttributeColor(a, r, g, b, alpha); +} + +uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeFeatures); + a->u.features = uiOpenTypeFeaturesClone(otf); + return a; +} + +const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a) +{ + return a->u.features; +} + +int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b) +{ + if (a == b) + return 1; + if (a->type != b->type) + return 0; + switch (a->type) { + case uiAttributeTypeFamily: + return uiprivStricmp(a->u.family, b->u.family); + case uiAttributeTypeSize: + // TODO is the use of == correct? + return a->u.size == b->u.size; + case uiAttributeTypeWeight: + return a->u.weight == b->u.weight; + case uiAttributeTypeItalic: + return a->u.italic == b->u.italic; + case uiAttributeTypeStretch: + return a->u.stretch == b->u.stretch; + case uiAttributeTypeUnderline: + return a->u.underline == b->u.underline; + case uiAttributeTypeUnderlineColor: + if (a->u.color.underlineColor != b->u.color.underlineColor) + return 0; + // fall through + case uiAttributeTypeColor: + case uiAttributeTypeBackground: + // TODO is the use of == correct? + return (a->u.color.r == b->u.color.r) && + (a->u.color.g == b->u.color.g) && + (a->u.color.b == b->u.color.b) && + (a->u.color.a == b->u.color.a); + case uiAttributeTypeFeatures: + return uiprivOpenTypeFeaturesEqual(a->u.features, b->u.features); + } + // TODO should not be reached + return 0; +} diff --git a/common/attrstr.h b/common/attrstr.h new file mode 100644 index 00000000..297add10 --- /dev/null +++ b/common/attrstr.h @@ -0,0 +1,4 @@ +// 19 february 2018 + +// attribute.c +extern int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b); diff --git a/ui_attrstr.h b/ui_attrstr.h index f180ad66..c2da9a4a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -51,7 +51,7 @@ _UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); // uiNewSizeAttribute() creates a new uiAttribute that changes the // size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); +_UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); // uiAttributeSize() returns the font size stored in a. It is an error to // call this on a uiAttribute that does not hold a font size. From 8b35d161449595403fe26240f84408e039700298 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Feb 2018 21:05:08 -0500 Subject: [PATCH 355/487] More notes. Also fixed a typo (thanks Nicole from Jul). --- _notes/windowsUWPGlass | 25 +++++++++++++++++++++++++ common/attribute.c | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 _notes/windowsUWPGlass diff --git a/_notes/windowsUWPGlass b/_notes/windowsUWPGlass new file mode 100644 index 00000000..debe47cd --- /dev/null +++ b/_notes/windowsUWPGlass @@ -0,0 +1,25 @@ +https://twitter.com/omgubuntu/status/962765197109841922 +https://github.com/DominicMaas/SoundByte/blob/master/SoundByte.UWP/Resources/Brushes.xaml +https://docs.microsoft.com/en-us/windows/uwp/design/style/acrylic +https://www.google.com/search?q=uwp+acrylic+winapi&ie=utf-8&oe=utf-8&client=firefox-b-1 +https://stackoverflow.com/questions/43931709/acrylic-material-in-win32-app +https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app +https://stackoverflow.com/questions/43699256/how-to-use-acrylic-accent-in-windows-10-creators-update +https://stackoverflow.com/questions/39135834/how-to-call-uwp-api-from-converted-win32-app-desktop-app-converter +https://stackoverflow.com/questions/32724187/how-do-you-set-the-glass-blend-colour-on-windows-10 +https://channel9.msdn.com/Events/Build/2017/B8034 +https://www.reddit.com/r/Windows10/comments/7lzyzc/since_the_taskbar_is_still_win32_and_it_can_have/ +https://thenextweb.com/microsoft/2017/05/15/microsoft-fluent-design-system-breaking-windows-10s-new-look/ +https://github.com/bbougot/AcrylicWPF +http://vhanla.codigobit.info/2015/07/enable-windows-10-aero-glass-aka-blur.html +http://undoc.airesoft.co.uk/user32.dll/SetWindowCompositionAttribute.php +http://undoc.airesoft.co.uk/user32.dll/GetWindowCompositionAttribute.php +https://msdn.microsoft.com/en-us/library/windows/desktop/aa969530(v=vs.85).aspx +https://github.com/bbougot/AcrylicWPF/blob/master/MainWindow.xaml.cs +https://www.google.com/search?q=%22WCA_ACCENT_POLICY%22&client=firefox-b-1&source=lnt&tbs=cdr%3A1%2Ccd_min%3A1%2F1%2F2001%2Ccd_max%3A12%2F31%2F2013&tbm= +https://github.com/sgothel/jogl/blob/master/src/nativewindow/native/win32/WindowsDWM.c +http://www.brandonfa.lk/win8/win8_devrel_head_x86/uxtheme.h +http://www.brandonfa.lk/win8/ +https://github.com/gamozolabs/pdblister +https://github.com/wbenny/pdbex +https://github.com/wbenny/pdbex/releases diff --git a/common/attribute.c b/common/attribute.c index 49dc860d..66120e99 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -33,7 +33,7 @@ static uiAttribute *newAttribute(uiAttributeType type) } void uiFreeAttribute(uiAttribute *a) -[ +{ switch (a->type) { case uiAttributeTypeFamily: uiprivFree(a->u.family); From 20a94937b31e412e115fe0e91f9184e5e45ef754 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Feb 2018 20:58:40 -0500 Subject: [PATCH 356/487] More TODOs. --- ui_attrstr.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index c2da9a4a..9b02035d 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -390,7 +390,8 @@ _UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribut // uiAttributes in s. It is an error to modify s in f. Within f, s still // owns the attribute; you can neither free it nor save it for later // use. -// TODO reword the above for consistency +// TODO reword the above for consistency (TODO and find out what I meant by that) +// TODO define an enumeration order (or mark it as undefined); also define how consecutive runs of identical attributes are handled here and sync with the definition of uiAttributedString itself _UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // TODO const correct this somehow (the implementation needs to mutate the structure) From dcaf69bc51ae6ec5c3b22e0699bf197b0029c81c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Feb 2018 20:38:06 -0500 Subject: [PATCH 357/487] Added a single cross-platform implementation of uiOpenTypeFeatures. --- common/opentype.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 common/opentype.c diff --git a/common/opentype.c b/common/opentype.c new file mode 100644 index 00000000..7ed1062c --- /dev/null +++ b/common/opentype.c @@ -0,0 +1,147 @@ +// 25 february 2018 +#include "uipriv.h" +#include "attrstr.h" + +struct feature { + char a; + char b; + char c; + char d; + uint32_t value; +}; + +struct uiOpenTypeFeatures { + struct feature *data; + size_t len; + size_t cap; +}; + +#define bytecount(n) ((n) * sizeof (struct feature)) + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiprivNew(uiOpenTypeFeatures); + otf->cap = 16; + otf->data = (struct feature *) uiprivAlloc(bytecount(otf->cap), "struct feature[]"); + otf->len = 0; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + uiprivFree(otf->data); + uiprivFree(otf); +} + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *ret; + + ret = uiprivNew(uiOpenTypeFeatures); + ret->len = otf->len; + ret->cap = otf->cap; + ret->data = (struct feature *) uiprivAlloc(bytecount(ret->cap), "struct feature[]"); + memset(ret->data, 0, bytecount(ret->cap)); + memmove(ret->data, otf->data, bytecount(ret->len)); + return ret; +} + +#define intdiff(a, b) (((int) (a)) - ((int) (b))) + +static int featurecmp(const void *a, const void *b) +{ + const struct feature *f = (const struct feature *) a; + const struct feature *g = (const struct feature *) b; + + if (f->a != g->a) + return intdiff(f->a, g->a); + if (f->b != g->b) + return intdiff(f->b, g->b); + if (f->c != g->c) + return intdiff(f->c, g->c); + return intdiff(f->d, g->d); +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ + struct feature *f; + + // replace existing value if any + f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + if (f != NULL) { + f->value = value; + return; + } + + // if we got here, the tag is new + if (otf->len == otf->cap) { + otf->cap *= 2; + otf->data = (struct feature *) uiprivRealloc(otf->data, bytecount(otf->cap), "struct feature[]"); + } + f = otf->data + otf->len; + f->a = a; + f->b = b; + f->c = c; + f->d = d; + f->value = value; + // TODO qsort here is overkill + otf->len++; + qsort(otf->data, otf->len, sizeof (struct feature), featurecmp); +} + +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ + struct feature *f; + ptrdiff_t index; + size_t count; + + f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + if (f == NULL) + return; + + index = f - otf->data; + count = otf->len - index - 1; + memmove(f + 1, f, bytecount(count)); + otf->len--; +} + +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ + const struct feature *f; + + f = (const struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + if (f == NULL) + return 0; + *value = f->value; + return 1; +} + +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ + size_t n; + const struct feature *p; + uiForEach ret; + + p = otf->data; + for (n = 0; n < otf->len; n++) { + ret = (*f)(cur->a, cur->b, cur->c, cur->d, + cur->value, data); + // TODO for all: require exact match? + if (ret == uiForEachStop) + return; + p++; + } +} + +int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) +{ + if (a == b) + return 1; + if (a->len != b->len) + return 0; + return memcmp(a->data, b->data, bytecount(a->len)) == 0; +} From 3f62cb5cee6eccb14c501c6ae42deee6edc3ab41 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Feb 2018 20:40:23 -0500 Subject: [PATCH 358/487] Oops; forgot to update attrstr.h in the last commit. Also changed a TODO to a LONGTERM in opentype.c. --- common/attrstr.h | 3 +++ common/opentype.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/attrstr.h b/common/attrstr.h index 297add10..f835a8a4 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -2,3 +2,6 @@ // attribute.c extern int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b); + +// opentype.c +extern int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); diff --git a/common/opentype.c b/common/opentype.c index 7ed1062c..335a2ad0 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -88,7 +88,7 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char f->c = c; f->d = d; f->value = value; - // TODO qsort here is overkill + // LONGTERM qsort here is overkill otf->len++; qsort(otf->data, otf->len, sizeof (struct feature), featurecmp); } From 70815d8d7b80966b68ff12c4d9db7004d27d0803 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 27 Feb 2018 23:44:50 -0500 Subject: [PATCH 359/487] Started writing a test suite for uiOpenTypeFeatures. So far it's just the test boilerplate. --- common/opentype_test.c | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 common/opentype_test.c diff --git a/common/opentype_test.c b/common/opentype_test.c new file mode 100644 index 00000000..784073a8 --- /dev/null +++ b/common/opentype_test.c @@ -0,0 +1,114 @@ +xx 27 february 2018 +#ifndef TODO_TEST +#error TODO this is where libui itself goes +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) +#define testingTest(Name) \ + void Test ## Name(testingT *t); \ + __attribute__((constructor)) static inline void testingprivCtorRegisterTest ## Name(void) { testingprivRegisterTest("Test" #Name, Test ## Name); } \ + void Test ## Name(testingT *t) +#else +#error unknown compiler; cannot continue +#endif + +extern int testingMain(void); + +typedef struct testingT testingT; +#define testingTErrorf(t, ...) testingprivTLogFull(t, __FILE__, __LINE__, __VA_ARGS__) +#define testingTErrorvf(t, format, ap) testingprivTLogFullv(t, __FILE__, __LINE__, format, ap) + +extern void testingprivRegisterTest(const char *, void (*)(testi +ngT *)); +extern void testingprivTLogFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTLogFullv(testingT *, const char *, int, const char *, va_list); + +#ifdef __cplusplus +} +#endif + +testingTest(Name) +{ +} + +int main(void) +{ + return testingMain(); +} + +#include +#include + +#define testingprivNew(T) ((T *) malloc(sizeof (T))) + +struct testingT { + const char *name; + void (*f)(testingT *); + int failed; + struct testingprivTest *next; +}; + +static testingT *tests = NULL; + +void testingprivRegisterTest(const char *name, void (*f)(testingT *)) +{ + testingT *t; + + t = testingprivNew(testingT) + t->name = name; + t->f = f; + t->failed = 0; + t->next = tests; + tests = t; +} + +int testingMain(void) +{ + testingT *t; + int anyFailed; + const char *status; + + if (tests == NULL) { + fprintf(stderr, "warning: no tests to run\n"); + xx imitate Go here (TODO confirm this) + return 0; + } + + anyFailed = 0; + for (t = tests; t != NULL; t = t->next) { + printf("=== RUN %s\n", t->name); + (*(t->f))(t); + status = "PASS"; + if (t->failed) { + status = "FAIL"; + anyFailed = 1; + } + printf("--- %s: %s (%s)\n", status, t->name, "TODO"); + } + + if (anyFailed) { + printf("FAIL\n"); + return 1; + } + printf("PASS\n"); + return 0; +} + +extern void testingprivTLogFull(testingT *t, const char *file, int line, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + testingprivTLogFullv(t, file, line, format, ap); + va_end(ap); +} + +void testingprivTLogFullv(testingT *t, const char *file, int line, const char *format, va_list ap) +{ +} From 5ab1266b5d1ee2afb147560416d55dd4d05fdf7d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 01:21:10 -0500 Subject: [PATCH 360/487] Finished the boilerplate for the uiOpenTypeFeatures test. --- common/opentype_test.c | 52 ++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index 784073a8..b494527b 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -1,10 +1,26 @@ -xx 27 february 2018 +// 27 february 2018 #ifndef TODO_TEST #error TODO this is where libui itself goes #endif #include +#undef testingprivBadLanguageVersion +#ifdef __cplusplus +// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong... +// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)? +#if __cplusplus < 201103L +#define testingprivBadLanguageVersion +#endif +#elif !defined(__STDC_VERSION__) +#define testingprivBadLanguageVersion +#elif __STDC_VERSION__ < 199901L +#define testingprivBadLanguageVersion +#endif +#ifdef testingprivBadLanguageVersion +#error sorry, TODO requires either C99 or TODO; cannot continue +#endif + #ifdef __cplusplus extern "C" { #endif @@ -21,13 +37,13 @@ extern "C" { extern int testingMain(void); typedef struct testingT testingT; -#define testingTErrorf(t, ...) testingprivTLogFull(t, __FILE__, __LINE__, __VA_ARGS__) -#define testingTErrorvf(t, format, ap) testingprivTLogFullv(t, __FILE__, __LINE__, format, ap) +#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) +#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) -extern void testingprivRegisterTest(const char *, void (*)(testi -ngT *)); -extern void testingprivTLogFull(testingT *, const char *, int, const char *, ...); -extern void testingprivTLogFullv(testingT *, const char *, int, const char *, va_list); +// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? +extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); #ifdef __cplusplus } @@ -35,6 +51,7 @@ extern void testingprivTLogFullv(testingT *, const char *, int, const char *, va testingTest(Name) { + printf("in the test!\n"); } int main(void) @@ -60,7 +77,7 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) { testingT *t; - t = testingprivNew(testingT) + t = testingprivNew(testingT); t->name = name; t->f = f; t->failed = 0; @@ -76,7 +93,7 @@ int testingMain(void) if (tests == NULL) { fprintf(stderr, "warning: no tests to run\n"); - xx imitate Go here (TODO confirm this) + // imitate Go here (TODO confirm this) return 0; } @@ -100,15 +117,26 @@ int testingMain(void) return 0; } -extern void testingprivTLogFull(testingT *t, const char *file, int line, const char *format, ...) +static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) +{ + // TODO extract filename from file + printf("\t%s:%d: ", file, line); + // TODO split into lines separated by \n\t\t and trimming trailing empty lines + vprintf(format, ap); + printf("\n"); +} + +void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) { va_list ap; va_start(ap, format); - testingprivTLogFullv(t, file, line, format, ap); + testingprivTErrorvfFull(t, file, line, format, ap); va_end(ap); } -void testingprivTLogFullv(testingT *t, const char *file, int line, const char *format, va_list ap) +void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) { + testingprivTDoLog(t, file, line, format, ap); + t->failed = 1; } From cf15dba2efb9d2d94f3214c47f850ed3e3495aee Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 01:22:16 -0500 Subject: [PATCH 361/487] Oops, missed a spot when cleaning up the previous commit. Fixed. --- common/opentype_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index b494527b..b883c53b 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -68,7 +68,7 @@ struct testingT { const char *name; void (*f)(testingT *); int failed; - struct testingprivTest *next; + testingT *next; }; static testingT *tests = NULL; From 010e8782861b8ac19be2e17cfbaabcfaa17373a5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 19:43:29 -0500 Subject: [PATCH 362/487] Wrote more of the testing framework, wrote the first test, and fixed compiler errors in opentype.c. --- common/opentype.c | 38 +++++++++++++++++++++++++++----------- common/opentype_test.c | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index 335a2ad0..f27907f6 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -1,6 +1,6 @@ // 25 february 2018 -#include "uipriv.h" -#include "attrstr.h" +//TODO#include "uipriv.h" +//TODO#include "attrstr.h" struct feature { char a; @@ -35,9 +35,7 @@ void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) uiprivFree(otf); } -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *ret; @@ -66,12 +64,27 @@ static int featurecmp(const void *a, const void *b) return intdiff(f->d, g->d); } +static struct feature mkkey(char a, char b, char c, char d) +{ + struct feature f; + + f.a = a; + f.b = b; + f.c = c; + f.d = d; + return f; +} + +#define find(pkey, otf) bsearch(pkey, otf->data, otf->len, sizeof (struct feature), featurecmp) + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { struct feature *f; + struct feature key; // replace existing value if any - f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + key = mkkey(a, b, c, d); + f = (struct feature *) find(&key, otf); if (f != NULL) { f->value = value; return; @@ -93,13 +106,15 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char qsort(otf->data, otf->len, sizeof (struct feature), featurecmp); } -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { struct feature *f; + struct feature key; ptrdiff_t index; size_t count; - f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + key = mkkey(a, b, c, d); + f = (struct feature *) find(&key, otf); if (f == NULL) return; @@ -112,8 +127,10 @@ _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { const struct feature *f; + struct feature key; - f = (const struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + key = mkkey(a, b, c, d); + f = (const struct feature *) find(&key, otf); if (f == NULL) return 0; *value = f->value; @@ -128,8 +145,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures p = otf->data; for (n = 0; n < otf->len; n++) { - ret = (*f)(cur->a, cur->b, cur->c, cur->d, - cur->value, data); + ret = (*f)(otf, p->a, p->b, p->c, p->d, p->value, data); // TODO for all: require exact match? if (ret == uiForEachStop) return; diff --git a/common/opentype_test.c b/common/opentype_test.c index b883c53b..3fe00079 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -2,6 +2,7 @@ #ifndef TODO_TEST #error TODO this is where libui itself goes #endif +#include #include @@ -18,7 +19,7 @@ #define testingprivBadLanguageVersion #endif #ifdef testingprivBadLanguageVersion -#error sorry, TODO requires either C99 or TODO; cannot continue +#error sorry, TODO requires either C99 or C++11; cannot continue #endif #ifdef __cplusplus @@ -49,9 +50,39 @@ extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, } #endif -testingTest(Name) +#include +#include +#include +#include +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; +typedef int uiForEach; +enum { uiForEachContinue, uiForEachStop }; +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); +#define uiprivNew(x) ((x *) malloc(sizeof (x))) +#define uiprivAlloc(x,y) malloc(x) +#define uiprivRealloc(x,y,z) realloc(x,y) +#define uiprivFree free +#include "opentype.c" + +testingTest(OpenTypeFeaturesAddGet) { - printf("in the test!\n"); + uiOpenTypeFeatures *otf; + char a, b, c, d; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) { + testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + goto out; + } + if (value != 12345) { + testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value); + goto out; + } + +out: + uiFreeOpenTypeFeatures(otf); } int main(void) From 8d92003426926ad9e29007b52abfa7cfc433a348 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 22:07:06 -0500 Subject: [PATCH 363/487] Broke apart the testing implementation code into their own files. --- common/opentype_test.c | 129 +-------------------------------------- common/testing.h | 63 +++++++++++++++++++ common/testing_testing.c | 83 +++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 128 deletions(-) create mode 100644 common/testing.h create mode 100644 common/testing_testing.c diff --git a/common/opentype_test.c b/common/opentype_test.c index 3fe00079..589353e8 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -3,52 +3,7 @@ #error TODO this is where libui itself goes #endif #include - -#include - -#undef testingprivBadLanguageVersion -#ifdef __cplusplus -// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong... -// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)? -#if __cplusplus < 201103L -#define testingprivBadLanguageVersion -#endif -#elif !defined(__STDC_VERSION__) -#define testingprivBadLanguageVersion -#elif __STDC_VERSION__ < 199901L -#define testingprivBadLanguageVersion -#endif -#ifdef testingprivBadLanguageVersion -#error sorry, TODO requires either C99 or C++11; cannot continue -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) -#define testingTest(Name) \ - void Test ## Name(testingT *t); \ - __attribute__((constructor)) static inline void testingprivCtorRegisterTest ## Name(void) { testingprivRegisterTest("Test" #Name, Test ## Name); } \ - void Test ## Name(testingT *t) -#else -#error unknown compiler; cannot continue -#endif - -extern int testingMain(void); - -typedef struct testingT testingT; -#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) -#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) - -// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? -extern void testingprivRegisterTest(const char *, void (*)(testingT *)); -extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); -extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); - -#ifdef __cplusplus -} -#endif +#include "testing.h" #include #include @@ -89,85 +44,3 @@ int main(void) { return testingMain(); } - -#include -#include - -#define testingprivNew(T) ((T *) malloc(sizeof (T))) - -struct testingT { - const char *name; - void (*f)(testingT *); - int failed; - testingT *next; -}; - -static testingT *tests = NULL; - -void testingprivRegisterTest(const char *name, void (*f)(testingT *)) -{ - testingT *t; - - t = testingprivNew(testingT); - t->name = name; - t->f = f; - t->failed = 0; - t->next = tests; - tests = t; -} - -int testingMain(void) -{ - testingT *t; - int anyFailed; - const char *status; - - if (tests == NULL) { - fprintf(stderr, "warning: no tests to run\n"); - // imitate Go here (TODO confirm this) - return 0; - } - - anyFailed = 0; - for (t = tests; t != NULL; t = t->next) { - printf("=== RUN %s\n", t->name); - (*(t->f))(t); - status = "PASS"; - if (t->failed) { - status = "FAIL"; - anyFailed = 1; - } - printf("--- %s: %s (%s)\n", status, t->name, "TODO"); - } - - if (anyFailed) { - printf("FAIL\n"); - return 1; - } - printf("PASS\n"); - return 0; -} - -static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) -{ - // TODO extract filename from file - printf("\t%s:%d: ", file, line); - // TODO split into lines separated by \n\t\t and trimming trailing empty lines - vprintf(format, ap); - printf("\n"); -} - -void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - testingprivTErrorvfFull(t, file, line, format, ap); - va_end(ap); -} - -void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) -{ - testingprivTDoLog(t, file, line, format, ap); - t->failed = 1; -} diff --git a/common/testing.h b/common/testing.h new file mode 100644 index 00000000..9d8c8006 --- /dev/null +++ b/common/testing.h @@ -0,0 +1,63 @@ +// 27 february 2018 + +#ifndef testingprivIncludeGuard_testing_h +#define testingprivIncludeGuard_testing_h + +#include + +#undef testingprivBadLanguageVersion +#ifdef __cplusplus +// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong... +// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)? +#if __cplusplus < 201103L +#define testingprivBadLanguageVersion +#endif +#elif !defined(__STDC_VERSION__) +#define testingprivBadLanguageVersion +#elif __STDC_VERSION__ < 199901L +#define testingprivBadLanguageVersion +#endif +#ifdef testingprivBadLanguageVersion +#error sorry, TODO requires either C99 or C++11; cannot continue +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) +#define testingprivMkCtor(name, reg) \ + __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, name); } +#elif defined(_MSC_VER) +#define testingprivMkCtorPrototype(name, reg) \ + static int name(void) testingprivCtor ## name(void) { reg(#name, name); return 0; } \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name; +#elif defined(__SUNPRO_C) +#define testingprivMkCtor(name, reg) \ + _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, name); } +#else +#error unknown compiler; cannot continue +#endif + +#define testingTest(Name) \ + void Test ## Name(testingT *t); \ + testingprivMkCtor(Test ## Name, testingprivRegisterTest) \ + void Test ## Name(testingT *t) + +extern int testingMain(void); + +typedef struct testingT testingT; +#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) +#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) + +// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? +extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/common/testing_testing.c b/common/testing_testing.c new file mode 100644 index 00000000..c8d0d986 --- /dev/null +++ b/common/testing_testing.c @@ -0,0 +1,83 @@ +// 27 february 2018 +#include +#include +#include "testing.h" + +#define testingprivNew(T) ((T *) malloc(sizeof (T))) + +struct testingT { + const char *name; + void (*f)(testingT *); + int failed; + testingT *next; +}; + +static testingT *tests = NULL; + +void testingprivRegisterTest(const char *name, void (*f)(testingT *)) +{ + testingT *t; + + t = testingprivNew(testingT); + t->name = name; + t->f = f; + t->failed = 0; + t->next = tests; + tests = t; +} + +int testingMain(void) +{ + testingT *t; + int anyFailed; + const char *status; + + if (tests == NULL) { + fprintf(stderr, "warning: no tests to run\n"); + // imitate Go here (TODO confirm this) + return 0; + } + + anyFailed = 0; + for (t = tests; t != NULL; t = t->next) { + printf("=== RUN %s\n", t->name); + (*(t->f))(t); + status = "PASS"; + if (t->failed) { + status = "FAIL"; + anyFailed = 1; + } + printf("--- %s: %s (%s)\n", status, t->name, "TODO"); + } + + if (anyFailed) { + printf("FAIL\n"); + return 1; + } + printf("PASS\n"); + return 0; +} + +static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) +{ + // TODO extract filename from file + printf("\t%s:%d: ", file, line); + // TODO split into lines separated by \n\t\t and trimming trailing empty lines + vprintf(format, ap); + printf("\n"); +} + +void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + testingprivTErrorvfFull(t, file, line, format, ap); + va_end(ap); +} + +void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) +{ + testingprivTDoLog(t, file, line, format, ap); + t->failed = 1; +} From d0db6f95942cb4bd353835131f85247dd05c06c6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 1 Mar 2018 20:25:36 -0500 Subject: [PATCH 364/487] Added early termination mechanics to the testing framework. --- common/testing.h | 43 +++++++++++++++++++++++++++++++++++----- common/testing_testing.c | 11 +++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/common/testing.h b/common/testing.h index 9d8c8006..10403c33 100644 --- a/common/testing.h +++ b/common/testing.h @@ -25,23 +25,43 @@ extern "C" { #endif -#if defined(__GNUC__) +#ifdef __cplusplus +#define testingprivMkScaffold(name) \ + static inline void testingprivScaffold ## name(testingT *t) \ + { \ + bool failedNow = false; \ + try { name(t); } \ + catch (testingprivFailNowException e) { failedNow = true; } \ + /* TODO see if we should catch other exceptions too */ \ + /* don't call this in the catch block as it calls longjmp() */ \ + if (failedNow) testingprivTDoFailNow(t); \ + } +#else +#define testingprivMkScaffold(name) \ + static inline void testingprivScaffold ## name(testingT *t) { name(t); } +#endif + +#if defined(__cplusplus) #define testingprivMkCtor(name, reg) \ - __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, name); } + static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name); +#elif defined(__GNUC__) +#define testingprivMkCtor(name, reg) \ + __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); } #elif defined(_MSC_VER) #define testingprivMkCtorPrototype(name, reg) \ - static int name(void) testingprivCtor ## name(void) { reg(#name, name); return 0; } \ + static int name(void) testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); return 0; } \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name; #elif defined(__SUNPRO_C) #define testingprivMkCtor(name, reg) \ - _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, name); } + _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); } #else -#error unknown compiler; cannot continue +#error unknown compiler for making constructors in C; cannot continue #endif #define testingTest(Name) \ void Test ## Name(testingT *t); \ + testingprivMkScaffold(Test ## Name) \ testingprivMkCtor(Test ## Name, testingprivRegisterTest) \ void Test ## Name(testingT *t) @@ -50,14 +70,27 @@ extern int testingMain(void); typedef struct testingT testingT; #define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) #define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) +#ifdef __cplusplus +#define testingTFailNow(t) (throw testingprivFailNowException()) +#else +#define testingTFailNow(t) testingprivTDoFailNow(t) +#endif // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); +extern void testingprivTDoFailNow(testingT *); #ifdef __cplusplus } +namespace { + class testingprivFailNowException {}; + class testingprivRegisterTestClass { + public: + testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); } + }; +} #endif #endif diff --git a/common/testing_testing.c b/common/testing_testing.c index c8d0d986..33e19bbe 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -1,6 +1,7 @@ // 27 february 2018 #include #include +#include #include "testing.h" #define testingprivNew(T) ((T *) malloc(sizeof (T))) @@ -9,6 +10,7 @@ struct testingT { const char *name; void (*f)(testingT *); int failed; + jmp_buf failNowBuf; testingT *next; }; @@ -41,7 +43,8 @@ int testingMain(void) anyFailed = 0; for (t = tests; t != NULL; t = t->next) { printf("=== RUN %s\n", t->name); - (*(t->f))(t); + if (setjmp(t->failNowBuf) == 0) + (*(t->f))(t); status = "PASS"; if (t->failed) { status = "FAIL"; @@ -81,3 +84,9 @@ void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char testingprivTDoLog(t, file, line, format, ap); t->failed = 1; } + +void testingprivTDoFailNow(testingT *t) +{ + t->failed = 1; + longjmp(t->failNowBuf, 1); +} From b5570040b0a859b479ba94241cf943fe778bbca3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 14:08:17 -0500 Subject: [PATCH 365/487] Added skipping and fatal to testing.h. --- common/testing.h | 22 +++++++++++++++++----- common/testing_testing.c | 31 +++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/common/testing.h b/common/testing.h index 10403c33..6a20a0a7 100644 --- a/common/testing.h +++ b/common/testing.h @@ -29,12 +29,14 @@ extern "C" { #define testingprivMkScaffold(name) \ static inline void testingprivScaffold ## name(testingT *t) \ { \ - bool failedNow = false; \ + bool failedNow = false, skippedNow = false; \ try { name(t); } \ catch (testingprivFailNowException e) { failedNow = true; } \ + catch (testingprivSkipNowException e) { skippedNow = true; } \ /* TODO see if we should catch other exceptions too */ \ - /* don't call this in the catch block as it calls longjmp() */ \ + /* don't call these in the catch blocks as they call longjmp() */ \ if (failedNow) testingprivTDoFailNow(t); \ + if (skippedNow) testingprivTDoSkipNow(t); \ } #else #define testingprivMkScaffold(name) \ @@ -68,24 +70,34 @@ extern "C" { extern int testingMain(void); typedef struct testingT testingT; -#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) -#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) +#define testingTLogf(t, ...) (testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)) +#define testingTLogvf(t, format, ap) (testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)) +#define testingTErrorf(t, ...) (testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)) +#define testingTErrorvf(t, format, ap) (testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)) +#define testingTFatalf(t, ...) ((testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (testingTFailNow(t))) +#define testingTFatalvf(t, format, ap) ((testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)), (testingTFailNow(t))) #ifdef __cplusplus #define testingTFailNow(t) (throw testingprivFailNowException()) +#define testingTSkipNow(t) (throw testingprivSkipNowException()) #else -#define testingTFailNow(t) testingprivTDoFailNow(t) +#define testingTFailNow(t) (testingprivTDoFailNow(t)) +#define testingTSkipNow(t) (testingprivTDoSkipNow(t)) #endif // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list); extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); extern void testingprivTDoFailNow(testingT *); +extern void testingprivTDoSkipNow(testingT *); #ifdef __cplusplus } namespace { class testingprivFailNowException {}; + class testingprivSkipNowException {}; class testingprivRegisterTestClass { public: testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); } diff --git a/common/testing_testing.c b/common/testing_testing.c index 33e19bbe..24d22b0b 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -10,7 +10,8 @@ struct testingT { const char *name; void (*f)(testingT *); int failed; - jmp_buf failNowBuf; + int skipped; + jmp_buf returnNowBuf; testingT *next; }; @@ -34,6 +35,7 @@ int testingMain(void) int anyFailed; const char *status; + // TODO see if this should run if all tests are skipped if (tests == NULL) { fprintf(stderr, "warning: no tests to run\n"); // imitate Go here (TODO confirm this) @@ -43,13 +45,15 @@ int testingMain(void) anyFailed = 0; for (t = tests; t != NULL; t = t->next) { printf("=== RUN %s\n", t->name); - if (setjmp(t->failNowBuf) == 0) + if (setjmp(t->returnNowBuf) == 0) (*(t->f))(t); status = "PASS"; if (t->failed) { status = "FAIL"; anyFailed = 1; - } + } else if (t->skipped) + // note that failed overrides skipped + status = "SKIP"; printf("--- %s: %s (%s)\n", status, t->name, "TODO"); } @@ -61,7 +65,16 @@ int testingMain(void) return 0; } -static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) +void testingprivTLogfFull(testingT *t, const char *file, int line, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + testingprivTLogvfFull(t, file, line, format, ap); + va_end(ap); +} + +void testingprivTLogvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) { // TODO extract filename from file printf("\t%s:%d: ", file, line); @@ -81,12 +94,18 @@ void testingprivTErrorfFull(testingT *t, const char *file, int line, const char void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) { - testingprivTDoLog(t, file, line, format, ap); + testingprivTLogvfFull(t, file, line, format, ap); t->failed = 1; } void testingprivTDoFailNow(testingT *t) { t->failed = 1; - longjmp(t->failNowBuf, 1); + longjmp(t->returnNowBuf, 1); +} + +void testingprivTDoSkipNow(testingT *t) +{ + t->skipped = 1; + longjmp(t->returnNowBuf, 1); } From 70db51d23bd90ab2a0474f265a0e770c6e5a4ee0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 14:22:34 -0500 Subject: [PATCH 366/487] Added testingTFail() and simplified the implementation of the logging macros. --- common/testing.h | 29 +++++++++++++++++++++-------- common/testing_testing.c | 15 +++------------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/common/testing.h b/common/testing.h index 6a20a0a7..fe63197b 100644 --- a/common/testing.h +++ b/common/testing.h @@ -70,12 +70,23 @@ extern "C" { extern int testingMain(void); typedef struct testingT testingT; -#define testingTLogf(t, ...) (testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)) -#define testingTLogvf(t, format, ap) (testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)) -#define testingTErrorf(t, ...) (testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)) -#define testingTErrorvf(t, format, ap) (testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)) -#define testingTFatalf(t, ...) ((testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (testingTFailNow(t))) -#define testingTFatalvf(t, format, ap) ((testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)), (testingTFailNow(t))) +#define testingTLogf(t, ...) \ + testingprivExpand(testingprivTLogfThen((void), t, __VA_ARGS__)) +#define testingTLogvf(t, format, ap) \ + testingprivTLogvfThen((void), t, format, ap) +#define testingTErrorf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTFail, t, __VA_ARGS__)) +#define testingTErrorvf(t, format, ap) \ + testingprivTLogvfThen(testingTFail, t, format, ap) +#define testingTFatalf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTFailNow, t, __VA_ARGS__)) +#define testingTFatalvf(t, format, ap) \ + testingprivTLogvfThen(testingTFailNow, t, format, ap) +#define testingTSkipf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTSkipNow, t, __VA_ARGS__)) +#define testingTSkipvf(t, format, ap) \ + testingprivTLogvfThen(testingTSkipNow, t, format, ap) +extern void testingTFail(testingT *t); #ifdef __cplusplus #define testingTFailNow(t) (throw testingprivFailNowException()) #define testingTSkipNow(t) (throw testingprivSkipNowException()) @@ -86,10 +97,12 @@ typedef struct testingT testingT; // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +// see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc +#define testingprivExpand(x) x +#define testingprivTLogfThen(then, t, ...) ((testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (then(t))) +#define testingprivTLogvfThen(then, t, format, ap) ((testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)), (then(t))) extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...); extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list); -extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); -extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); extern void testingprivTDoFailNow(testingT *); extern void testingprivTDoSkipNow(testingT *); diff --git a/common/testing_testing.c b/common/testing_testing.c index 24d22b0b..934daa5f 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -25,6 +25,7 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) t->name = name; t->f = f; t->failed = 0; + t->skipped = 0; t->next = tests; tests = t; } @@ -83,24 +84,14 @@ void testingprivTLogvfFull(testingT *t, const char *file, int line, const char * printf("\n"); } -void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) +void testingTFail(testingT *t) { - va_list ap; - - va_start(ap, format); - testingprivTErrorvfFull(t, file, line, format, ap); - va_end(ap); -} - -void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) -{ - testingprivTLogvfFull(t, file, line, format, ap); t->failed = 1; } void testingprivTDoFailNow(testingT *t) { - t->failed = 1; + testingTFail(t); longjmp(t->returnNowBuf, 1); } From 6c29932efe09a8cdfaaddeb028b87942b250eab6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 16:24:10 -0500 Subject: [PATCH 367/487] Added testingTDefer(). Now we can actually write the rest of these tests. --- common/opentype_test.c | 22 +++++++++----------- common/testing.h | 2 ++ common/testing_testing.c | 44 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index 589353e8..c374154e 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -19,25 +19,23 @@ typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf #define uiprivFree free #include "opentype.c" +static void freeOpenType(void *otf) +{ + uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) otf); +} + testingTest(OpenTypeFeaturesAddGet) { uiOpenTypeFeatures *otf; - char a, b, c, d; uint32_t value; otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); - if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) { - testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); - goto out; - } - if (value != 12345) { - testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value); - goto out; - } - -out: - uiFreeOpenTypeFeatures(otf); + if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) + testingTFatalf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + if (value != 12345) + testingTFatalf(t, "feature abcd: got %" PRIu32 ", want 12345", value); } int main(void) diff --git a/common/testing.h b/common/testing.h index fe63197b..15ea2629 100644 --- a/common/testing.h +++ b/common/testing.h @@ -94,6 +94,8 @@ extern void testingTFail(testingT *t); #define testingTFailNow(t) (testingprivTDoFailNow(t)) #define testingTSkipNow(t) (testingprivTDoSkipNow(t)) #endif +// TODO should the defered function also have t passed to it? +extern void testingTDefer(testingT *t, void (*f)(void *data), void *data); // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); diff --git a/common/testing_testing.c b/common/testing_testing.c index 934daa5f..6139ef75 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -6,12 +6,20 @@ #define testingprivNew(T) ((T *) malloc(sizeof (T))) +struct defer { + void (*f)(void *); + void *data; + struct defer *next; +}; + struct testingT { const char *name; void (*f)(testingT *); int failed; int skipped; jmp_buf returnNowBuf; + struct defer *defers; + int defersRun; testingT *next; }; @@ -26,10 +34,23 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) t->f = f; t->failed = 0; t->skipped = 0; + t->defers = NULL; + t->defersRun = 0; t->next = tests; tests = t; } +static void runDefers(testingT *t) +{ + struct defer *d; + + if (t->defersRun) + return; + t->defersRun = 1; + for (d = t->defers; d != NULL; d = d->next) + (*(d->f))(d->data); +} + int testingMain(void) { testingT *t; @@ -48,6 +69,7 @@ int testingMain(void) printf("=== RUN %s\n", t->name); if (setjmp(t->returnNowBuf) == 0) (*(t->f))(t); + runDefers(t); status = "PASS"; if (t->failed) { status = "FAIL"; @@ -89,14 +111,32 @@ void testingTFail(testingT *t) t->failed = 1; } +static void returnNow(testingT *t) +{ + // run defers before calling longjmp() just to be safe + runDefers(t); + longjmp(t->returnNowBuf, 1); +} + void testingprivTDoFailNow(testingT *t) { testingTFail(t); - longjmp(t->returnNowBuf, 1); + returnNow(t); } void testingprivTDoSkipNow(testingT *t) { t->skipped = 1; - longjmp(t->returnNowBuf, 1); + returnNow(t); +} + +void testingTDefer(testingT *t, void (*f)(void *data), void *data) +{ + struct defer *d; + + d = testingprivNew(struct defer); + d->f = f; + d->data = data; + d->next = t->defers; + t->defers = d; } From 4179ff86c2bb11009067c9c7168e696b2f84ce94 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 17:01:40 -0500 Subject: [PATCH 368/487] Added more test cases. Also more TODOs in testing_testing.c. --- common/opentype_test.c | 77 ++++++++++++++++++++++++++++++++++++++-- common/testing_testing.c | 2 ++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index c374154e..0c96108c 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -27,15 +27,86 @@ static void freeOpenType(void *otf) testingTest(OpenTypeFeaturesAddGet) { uiOpenTypeFeatures *otf; + int got; uint32_t value; otf = uiNewOpenTypeFeatures(); testingTDefer(t, freeOpenType, otf); uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); - if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) - testingTFatalf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + got = uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value); + + if (!got) + testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + else if (value != 12345) + testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value); +} + +testingTest(OpenTypeFeaturesRemove) +{ + uiOpenTypeFeatures *otf; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + uiOpenTypeFeaturesRemove(otf, 'a', 'b', 'c', 'd'); + + if (uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() succeeded in getting deleted feature; value %" PRIu32, value); +} + +testingTest(OpenTypeFeaturesCloneAdd) +{ + uiOpenTypeFeatures *otf, *otf2; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + otf2 = uiOpenTypeFeaturesClone(otf); + testingTDefer(t, freeOpenType, otf2); + uiOpenTypeFeaturesAdd(otf2, 'q', 'w', 'e', 'r', 56789); + + if (uiOpenTypeFeaturesGet(otf, 'q', 'w', 'e', 'r', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on original succeeded in getting feature added to clone; value %" PRIu32, value); +} + +testingTest(OpenTypeFeaturesCloneModify) +{ + uiOpenTypeFeatures *otf, *otf2; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + otf2 = uiOpenTypeFeaturesClone(otf); + testingTDefer(t, freeOpenType, otf2); + uiOpenTypeFeaturesAdd(otf2, 'a', 'b', 'c', 'd', 56789); + + uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value); if (value != 12345) - testingTFatalf(t, "feature abcd: got %" PRIu32 ", want 12345", value); + testingTErrorf(t, "uiOpenTypeFeaturesGet() on original: got %" PRIu32 ", want 12345", value); + uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value); + if (value != 56789) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone: got %" PRIu32 ", want 56789", value); +} + +testingTest(OpenTypeFeaturesCloneRemove) +{ + uiOpenTypeFeatures *otf, *otf2; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + otf2 = uiOpenTypeFeaturesClone(otf); + testingTDefer(t, freeOpenType, otf2); + uiOpenTypeFeaturesRemove(otf2, 'a', 'b', 'c', 'd'); + + if (uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone succeeded in getting feature removed from clone; value %" PRIu32, value); + if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on original failed to get feature removed from clone"); } int main(void) diff --git a/common/testing_testing.c b/common/testing_testing.c index 6139ef75..eb334648 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -36,6 +36,7 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) t->skipped = 0; t->defers = NULL; t->defersRun = 0; + // TODO add in the order called t->next = tests; tests = t; } @@ -137,6 +138,7 @@ void testingTDefer(testingT *t, void (*f)(void *data), void *data) d = testingprivNew(struct defer); d->f = f; d->data = data; + // add to the head of the list so defers are run in reverse order of how they were added d->next = t->defers; t->defers = d; } From 2822dbcebc8756c3699147a867fbd17cfcebf3d2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:27:01 -0500 Subject: [PATCH 369/487] Decided to keep the existing attrlist.c, but updated it to the new attribute API and uipriv naming convention. Also resolved some TODOs along the way and decided that when in attribute lists, uiAttributes will be refcounted. --- common/OLD_uipriv_attrstr.h | 13 -- common/attribute.c | 31 ++++- common/{OLD_attrlist.c => attrlist.c} | 172 ++++++++------------------ common/attrstr.h | 14 +++ common/opentype.c | 5 +- 5 files changed, 98 insertions(+), 137 deletions(-) rename common/{OLD_attrlist.c => attrlist.c} (78%) diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h index 6a1a63ce..cc67e276 100644 --- a/common/OLD_uipriv_attrstr.h +++ b/common/OLD_uipriv_attrstr.h @@ -17,19 +17,6 @@ extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); -// attrlist.c -struct attrlist; -extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); -extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); -extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); -extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); -extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); -extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); -// TODO move these to the top like everythng else -extern struct attrlist *attrlistNew(void); -extern void attrlistFree(struct attrlist *alist); - // drawtext.c struct caretDrawParams { double r; diff --git a/common/attribute.c b/common/attribute.c index 66120e99..da137349 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -1,8 +1,11 @@ // 19 february 2018 +#include "../ui.h" #include "uipriv.h" #include "attrstr.h" struct uiAttribute { + int owned; + size_t refcount; uiAttributeType type; union { const char *family; @@ -28,11 +31,21 @@ static uiAttribute *newAttribute(uiAttributeType type) uiAttribute *a; a = uiprivNew(uiAttribute); + a->ownedByUser = 1; + a->refcount = 0; a->type = type; return a; } -void uiFreeAttribute(uiAttribute *a) +// returns a to allow expressions like b = uiprivAttributeRetain(a) +uiAttribute *uiprivAttributeRetain(uiAttribute *a) +{ + a->ownedByUser = 0; + a->refcount++; + return a; +} + +static void destroy(uiAttribute *a) { switch (a->type) { case uiAttributeTypeFamily: @@ -45,6 +58,22 @@ void uiFreeAttribute(uiAttribute *a) uiprivFree(a); } +void uiprivAttributeRelease(uiAttribute *a) +{ + if (a->ownedByUser) + /* TODO implementation bug: we can't release an attribute we don't own */; + a->refcount--; + if (a->refcount == 0) + destroy(a); +} + +void uiFreeAttribute(uiAttribute *a) +{ + if (!a->ownedByUser) + /* TODO user bug: you can't free an attribute you don't own */; + destroy(a); +} + uiAttributeType uiAttributeGetType(const uiAttribute *a) { return a->type; diff --git a/common/OLD_attrlist.c b/common/attrlist.c similarity index 78% rename from common/OLD_attrlist.c rename to common/attrlist.c index 53217754..4fdb86f8 100644 --- a/common/OLD_attrlist.c +++ b/common/attrlist.c @@ -1,6 +1,7 @@ // 16 december 2016 #include "../ui.h" #include "uipriv.h" +#include "attrstr.h" /* An attribute list is a doubly linked list of attributes. @@ -12,62 +13,21 @@ The linked list is not a ring; alist->fist->prev == NULL and alist->last->next = TODO verify that this disallows attributes of length zero */ -struct attr { - uiAttributeSpec spec; +struct uiprivAttrList { + uiAttribute *val; size_t start; size_t end; struct attr *prev; struct attr *next; }; -struct attrlist { +uiprivAttrList { struct attr *first; struct attr *last; }; -// We need to make local copies of any pointers in uiAttributeSpec. -// If we don't do this, we'll wind up leaking stuff, or worse, prematurely freeing stuff. -// TODO ensure this is docmented -static void attrSetSpec(struct attr *a, uiAttributeSpec *spec) -{ - const char *family; - char *familyCopy; - - a->spec = *spec; - switch (a->spec.Type) { - case uiAttributeFamily: - // TODO UTF-8 validate this? - family = a->spec.Family; - familyCopy = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttributeSpec.Family copy)"); - strcpy(familyCopy, family); - a->spec.Family = familyCopy; - break; - case uiAttributeFeatures: - a->spec.Features = uiOpenTypeFeaturesClone(a->spec.Features); - break; - } -} - -static void attrCopySpec(struct attr *dest, struct attr *src) -{ - attrSetSpec(dest, &(src->spec)); -} - -// Likewise, this is needed to clean up a spec with a pointer when finished. -static void attrClearSpec(struct attr *a) -{ - switch (a->spec.Type) { - case uiAttributeFamily: - uiFree((char *) (a->spec.Family)); - break; - case uiAttributeFeatures: - uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) (a->spec.Features)); - break; - } -} - // if before is NULL, add to the end of the list -static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) +static void attrInsertBefore(uiprivAttrList *alist, struct attr *a, struct attr *before) { // if the list is empty, this is the first item if (alist->first == NULL) { @@ -131,7 +91,7 @@ static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) } // returns the old a->next, for forward iteration -static struct attr *attrUnlink(struct attrlist *alist, struct attr *a) +static struct attr *attrUnlink(uiprivAttrList *alist, struct attr *a) { struct attr *p, *n; @@ -165,13 +125,13 @@ static struct attr *attrUnlink(struct attrlist *alist, struct attr *a) } // returns the old a->next, for forward iteration -static struct attr *attrDelete(struct attrlist *alist, struct attr *a) +static struct attr *attrDelete(uiprivAttrList *alist, struct attr *a) { struct attr *next; next = attrUnlink(alist, a); - attrClearSpec(a); - uiFree(a); + uiprivAttributeRelease(a->val); + uiprivFree(a); return next; } @@ -188,7 +148,7 @@ static struct attr *attrDelete(struct attrlist *alist, struct attr *a) // Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. // // In all cases, the return value is the next attribute to look at in a forward sequential loop. -static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail) +static struct attr *attrDropRange(uiprivAttrList *alist, struct attr *a, size_t start, size_t end, struct attr **tail) { struct attr *b; @@ -219,8 +179,8 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t } // we'll need to split the attribute into two - b = uiNew(struct attr); - attrCopySpec(b, a); + b = uiprivNew(struct attr); + b->val = uiprivAttributeRetain(a->val); b->start = end; b->end = a->end; *tail = b; @@ -229,7 +189,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t return a->next; } -static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_t end) +static void attrGrow(uiprivAttrList *alist, struct attr *a, size_t start, size_t end) { struct attr *before; @@ -251,7 +211,7 @@ static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_ } // returns the right side of the split, which is unlinked, or NULL if no split was done -static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t at) +static struct attr *attrSplitAt(uiprivAttrList *alist, struct attr *a, size_t at) { struct attr *b; @@ -263,8 +223,8 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a if (at >= a->end) return NULL; - b = uiNew(struct attr); - attrCopySpec(b, a); + b = uiprivNew(struct attr); + b->val = uiprivAttributeRetain(a->val); b->start = at; b->end = a->end; @@ -282,7 +242,7 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a // // In all cases, the return value is the next attribute to look at in a forward sequential loop. // TODO rewrite this comment -static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size_t start, size_t end) +static struct attr *attrDeleteRange(uiprivAttrList *alist, struct attr *a, size_t start, size_t end) { size_t ostart, oend; size_t count; @@ -327,48 +287,37 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) +uiprivAttrList *uiprivNewAttrList(void) { - if (attr->spec.Type != spec->Type) - return 0; - switch (attr->spec.Type) { - case uiAttributeFamily: - // TODO should this be case-insensitive? - return strcmp(attr->spec.Family, spec->Family) == 0; - case uiAttributeSize: - // TODO use a closest match? - return attr->spec.Double == spec->Double; - case uiAttributeUnderlineColor: - if (attr->spec.Value != spec->Value) - return 0; - if (attr->spec.Value != uiDrawUnderlineColorCustom) - return 1; - // otherwise fall through - case uiAttributeColor: - case uiAttributeBackground: - // TODO use a closest match? - return attr->spec.R == spec->R && - attr->spec.G == spec->G && - attr->spec.B == spec->B && - attr->spec.A == spec->A; - case uiAttributeFeatures: - // TODO rename it to uiAttributeOpenTypeFeatures? - return uiOpenTypeFeaturesEqual(attr->spec.Features, spec->Features); - } - // handles the rest - return attr->spec.Value == spec->Value; + return uiprivNew(uiprivAttrList); } -void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end) +void uiprivFreeAttrList(uiprivAttrList *alist) +{ + struct attr *a, *next; + + a = alist->first; + while (a != NULL) { + next = a->next; + uiprivAttributeRelease(a->val); + uiprivFree(a); + a = next; + } + uiprivFree(alist); +} + +void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end) { struct attr *a; struct attr *before; struct attr *tail = NULL; int split = 0; + uiAttributeType valtype; // first, figure out where in the list this should go // in addition, if this attribute overrides one that already exists, split that one apart so this one can take over before = alist->first; + valtype = uiAttributeGetType(val); while (before != NULL) { size_t lstart, lend; @@ -380,8 +329,8 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size if (split) goto next; - // should we split this? - if (before->spec.Type != spec->Type) + // should we split this attribute? + if (uiAttributeGetType(before->val) != valtype) goto next; lstart = start; lend = end; @@ -390,7 +339,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? - if (specsIdentical(before, spec)) { + if (uiprivAttributeEqual(before->val, val)) { attrGrow(alist, before, start, end); return; } @@ -404,8 +353,8 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size } // if we got here, we know we have to add the attribute before before - a = uiNew(struct attr); - attrSetSpec(a, spec); + a = uiprivNew(struct attr); + a->val = uiprivAttributeRetain(val); a->start = start; a->end = end; attrInsertBefore(alist, a, before); @@ -420,7 +369,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size attrInsertBefore(alist, tail, before); } -void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count) +void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count) { struct attr *a; struct attr *tails = NULL; @@ -533,7 +482,7 @@ which results in our algorithm: move end up */ // TODO does this ensure the list remains sorted? -void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) +void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count) { struct attr *a; @@ -549,10 +498,10 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t // TODO replace at point with — replaces with first character's attributes -void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) +void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttributeType type, size_t start, size_t end) { struct attr *a; - struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tails = NULL; // see uiprivAttrListInsertCharactersUnattributed() above struct attr *tailsAt = NULL; a = alist->first; @@ -567,7 +516,7 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st // and at this point we're done, so break; } - if (a->spec.Type != type) + if (uiAttributeGetType(a->val) != type) goto next; lstart = start; lend = end; @@ -596,10 +545,10 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st } // TODO merge this with the above -void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) +void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end) { struct attr *a; - struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tails = NULL; // see uiprivAttrListInsertCharactersUnattributed() above struct attr *tailsAt = NULL; a = alist->first; @@ -640,7 +589,7 @@ void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) } } -void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end) { struct attr *a; @@ -649,34 +598,15 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) a = attrDeleteRange(alist, a, start, end); } -void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; uiForEach ret; for (a = alist->first; a != NULL; a = a->next) { - ret = (*f)(s, &(a->spec), a->start, a->end, data); + ret = (*f)(s, a->val, a->start, a->end, data); if (ret == uiForEachStop) // TODO for all: break or return? break; } } - -struct attrlist *attrlistNew(void) -{ - return uiNew(struct attrlist); -} - -void attrlistFree(struct attrlist *alist) -{ - struct attr *a, *next; - - a = alist->first; - while (a != NULL) { - next = a->next; - attrClearSpec(a); - uiFree(a); - a = next; - } - uiFree(alist); -} diff --git a/common/attrstr.h b/common/attrstr.h index f835a8a4..3017111b 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,7 +1,21 @@ // 19 february 2018 // attribute.c +extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); +extern void uiprivAttributeRelease(uiAttribute *a); extern int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b); // opentype.c extern int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); + +// attrlist.c +typedef struct uiprivAttrList uiprivAttrList; +extern uiprivAttrList *uiprivNewAttrList(void); +extern void uiprivFreeAttrList(uiprivAttrList *alist); +extern void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end); +extern void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count); +extern void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count); +extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute type, size_t start, size_t end); +extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); +extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); +extern void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/common/opentype.c b/common/opentype.c index f27907f6..625f5852 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -1,6 +1,7 @@ // 25 february 2018 -//TODO#include "uipriv.h" -//TODO#include "attrstr.h" +#include "../ui.h" +#include "uipriv.h" +#include "attrstr.h" struct feature { char a; From dcf67262396d2021c19b9b169dae4422c7e65631 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:29:45 -0500 Subject: [PATCH 370/487] More TODOs. --- common/attribute.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/attribute.c b/common/attribute.c index da137349..79273bef 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -38,6 +38,7 @@ static uiAttribute *newAttribute(uiAttributeType type) } // returns a to allow expressions like b = uiprivAttributeRetain(a) +// TODO would this allow us to copy attributes between strings in a foreach func, and if so, should that be allowed? uiAttribute *uiprivAttributeRetain(uiAttribute *a) { a->ownedByUser = 0; From 3337f06e2e6b147daa949acfa7eb649d833f12a8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:32:18 -0500 Subject: [PATCH 371/487] Oops. --- common/attrlist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 4fdb86f8..7a85aff9 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -13,7 +13,7 @@ The linked list is not a ring; alist->fist->prev == NULL and alist->last->next = TODO verify that this disallows attributes of length zero */ -struct uiprivAttrList { +struct attr { uiAttribute *val; size_t start; size_t end; @@ -21,7 +21,7 @@ struct uiprivAttrList { struct attr *next; }; -uiprivAttrList { +struct uiprivAttrList { struct attr *first; struct attr *last; }; From 766f3a0cb2ac763754aaf3bb08aada8bc5641591 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:35:29 -0500 Subject: [PATCH 372/487] Moved the unit tests out of the way for now. We'll fill them in later. --- {common => _future/unittest}/opentype_test.c | 0 {common => _future/unittest}/testing.h | 0 {common => _future/unittest}/testing_testing.c | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {common => _future/unittest}/opentype_test.c (100%) rename {common => _future/unittest}/testing.h (100%) rename {common => _future/unittest}/testing_testing.c (100%) diff --git a/common/opentype_test.c b/_future/unittest/opentype_test.c similarity index 100% rename from common/opentype_test.c rename to _future/unittest/opentype_test.c diff --git a/common/testing.h b/_future/unittest/testing.h similarity index 100% rename from common/testing.h rename to _future/unittest/testing.h diff --git a/common/testing_testing.c b/_future/unittest/testing_testing.c similarity index 100% rename from common/testing_testing.c rename to _future/unittest/testing_testing.c From f025783632641d67af81728d6b964d1f54ef7496 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 22:02:18 -0500 Subject: [PATCH 373/487] Migrated attrstr.c back like we just did to attrlist.c. RIP "graphemes()" --- common/OLD_uipriv_attrstr.h | 7 ----- common/{OLD_attrstr.c => attrstr.c} | 40 +++++++++++++++-------------- common/attrstr.h | 7 +++++ 3 files changed, 28 insertions(+), 26 deletions(-) rename common/{OLD_attrstr.c => attrstr.c} (87%) diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h index cc67e276..f2a1b936 100644 --- a/common/OLD_uipriv_attrstr.h +++ b/common/OLD_uipriv_attrstr.h @@ -10,13 +10,6 @@ extern struct graphemes *graphemes(void *s, size_t len); // TODO split these into a separate header file? -// attrstr.c -extern const uint16_t *attrstrUTF16(uiAttributedString *s); -extern size_t attrstrUTF16Len(uiAttributedString *s); -extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); -extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); -extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); - // drawtext.c struct caretDrawParams { double r; diff --git a/common/OLD_attrstr.c b/common/attrstr.c similarity index 87% rename from common/OLD_attrstr.c rename to common/attrstr.c index e34b7d27..7b774cfc 100644 --- a/common/OLD_attrstr.c +++ b/common/attrstr.c @@ -1,12 +1,13 @@ // 3 december 2016 #include "../ui.h" #include "uipriv.h" +#include "attrstr.h" struct uiAttributedString { char *s; size_t len; - struct attrlist *attrs; + uiprivAttrList *attrs; // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator // this ensures no one platform has a speed advantage (sorry GTK+) @@ -17,7 +18,7 @@ struct uiAttributedString { size_t *u16tou8; // this is lazily created to keep things from getting *too* slow - struct graphemes *graphemes; + uiprivGraphemes *graphemes; }; static void resize(uiAttributedString *s, size_t u8, size_t u16) @@ -35,21 +36,21 @@ uiAttributedString *uiNewAttributedString(const char *initialString) uiAttributedString *s; s = uiNew(uiAttributedString); - s->attrs = attrlistNew(); + s->attrs = uiprivNewAttrList(); uiAttributedStringAppendUnattributed(s, initialString); return s; } -// TODO make sure that all implementations of graphemes() work fine with empty strings; in particular, the Windows one might not +// TODO make sure that all implementations of uiprivNewGraphemes() work fine with empty strings; in particular, the Windows one might not static void recomputeGraphemes(uiAttributedString *s) { if (s->graphemes != NULL) return; - if (graphemesTakesUTF16()) { - s->graphemes = graphemes(s->u16, s->u16len); + if (uiprivGraphemesTakesUTF16()) { + s->graphemes = uiprivNewGraphemes(s->u16, s->u16len); return; } - s->graphemes = graphemes(s->s, s->len); + s->graphemes = uiprivNewGraphemes(s->s, s->len); } static void invalidateGraphemes(uiAttributedString *s) @@ -64,7 +65,7 @@ static void invalidateGraphemes(uiAttributedString *s) void uiFreeAttributedString(uiAttributedString *s) { - attrlistFree(s->attrs); + uiprivFreeAttrList(s->attrs); invalidateGraphemes(s); uiFree(s->u16tou8); uiFree(s->u8tou16); @@ -94,6 +95,7 @@ static void u8u16len(const char *str, size_t *n8, size_t *n16) while (*str) { str = utf8DecodeRune(str, 0, &rune); // TODO document the use of the function vs a pointer subtract here + // TODO also we need to consider namespace collision with utf.h... *n8 += utf8EncodeRune(rune, buf); *n16 += utf16EncodeRune(rune, buf16); } @@ -216,7 +218,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u16tou8[at16 + oldn16 + i] += s->len - oldlen; // and finally do the attributes - attrlistInsertCharactersUnattributed(s->attrs, at, n8); + uiprivAttrListInsertCharactersUnattributed(s->attrs, at, n8); } // TODO document that end is the first index that will be maintained @@ -271,7 +273,7 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) s->u16[start16 + count16] = 0; // fix up attributes - attrlistRemoveCharacters(s->attrs, start, end); + uiprivAttrListRemoveCharacters(s->attrs, start, end); // and finally resize resize(s, start + count, start16 + count16); @@ -287,7 +289,7 @@ size_t uiAttributedStringNumGraphemes(uiAttributedString *s) size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos) { recomputeGraphemes(s); - if (graphemesTakesUTF16()) + if (uiprivGraphemesTakesUTF16()) pos = s->u8tou16[pos]; return s->graphemes->pointsToGraphemes[pos]; } @@ -296,41 +298,41 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) { recomputeGraphemes(s); pos = s->graphemes->graphemesToPoints[pos]; - if (graphemesTakesUTF16()) + if (uiprivGraphemesTakesUTF16()) pos = s->u16tou8[pos]; return pos; } void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) { - attrlistInsertAttribute(s->attrs, spec, start, end); + uiprivAttrListInsertAttribute(s->attrs, spec, start, end); } // LONGTERM introduce an iterator object instead? void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { - attrlistForEach(s->attrs, s, f, data); + uiprivAttrListForEach(s->attrs, s, f, data); } // helpers for platform-specific code -const uint16_t *attrstrUTF16(uiAttributedString *s) +const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s) { return s->u16; } -size_t attrstrUTF16Len(uiAttributedString *s) +size_t uiprivAttributedStringUTF16Len(uiAttributedString *s) { return s->u16len; } // TODO is this still needed given the below? -size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n) +size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n) { return s->u8tou16[n]; } -size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; @@ -342,7 +344,7 @@ size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n) return out; } -size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; diff --git a/common/attrstr.h b/common/attrstr.h index 3017111b..b71e1864 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -19,3 +19,10 @@ extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute typ extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + +// attrstr.c +extern const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s); +extern size_t uiprivAttributedStringUTF16Len(uiAttributedString *s); +extern size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n); +extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n); From 77c07075e336e793a5c7de651fa4a03cad78b829 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 10:46:00 -0500 Subject: [PATCH 374/487] Updated grapheme function names in attrsr.h and updated allocator function names in attrstr.c. --- common/OLD_uipriv_attrstr.h | 8 -------- common/attrstr.c | 34 +++++++++++++++++----------------- common/attrstr.h | 9 +++++++++ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h index f2a1b936..ec920954 100644 --- a/common/OLD_uipriv_attrstr.h +++ b/common/OLD_uipriv_attrstr.h @@ -1,12 +1,4 @@ -// for attrstr.c -struct graphemes { - size_t len; - size_t *pointsToGraphemes; - size_t *graphemesToPoints; -}; -extern int graphemesTakesUTF16(void); -extern struct graphemes *graphemes(void *s, size_t len); // TODO split these into a separate header file? diff --git a/common/attrstr.c b/common/attrstr.c index 7b774cfc..73246651 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -18,24 +18,24 @@ struct uiAttributedString { size_t *u16tou8; // this is lazily created to keep things from getting *too* slow - uiprivGraphemes *graphemes; + struct graphemes *graphemes; }; static void resize(uiAttributedString *s, size_t u8, size_t u16) { s->len = u8; - s->s = (char *) uiRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)"); - s->u8tou16 = (size_t *) uiRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); + s->s = (char *) uiprivRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)"); + s->u8tou16 = (size_t *) uiprivRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); s->u16len = u16; - s->u16 = (uint16_t *) uiRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)"); - s->u16tou8 = (size_t *) uiRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); + s->u16 = (uint16_t *) uiprivRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)"); + s->u16tou8 = (size_t *) uiprivRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); } uiAttributedString *uiNewAttributedString(const char *initialString) { uiAttributedString *s; - s = uiNew(uiAttributedString); + s = uiprivNew(uiAttributedString); s->attrs = uiprivNewAttrList(); uiAttributedStringAppendUnattributed(s, initialString); return s; @@ -57,9 +57,9 @@ static void invalidateGraphemes(uiAttributedString *s) { if (s->graphemes == NULL) return; - uiFree(s->graphemes->pointsToGraphemes); - uiFree(s->graphemes->graphemesToPoints); - uiFree(s->graphemes); + uiprivFree(s->graphemes->pointsToGraphemes); + uiprivFree(s->graphemes->graphemesToPoints); + uiprivFree(s->graphemes); s->graphemes = NULL; } @@ -67,11 +67,11 @@ void uiFreeAttributedString(uiAttributedString *s) { uiprivFreeAttrList(s->attrs); invalidateGraphemes(s); - uiFree(s->u16tou8); - uiFree(s->u8tou16); - uiFree(s->u16); - uiFree(s->s); - uiFree(s); + uiprivFree(s->u16tou8); + uiprivFree(s->u8tou16); + uiprivFree(s->u16); + uiprivFree(s->s); + uiprivFree(s); } const char *uiAttributedStringString(const uiAttributedString *s) @@ -106,7 +106,7 @@ void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str uiAttributedStringInsertAtUnattributed(s, str, s->len); } -// this works (and returns true, which is what we want) at s->len too because s->s[s->len] is always going to be 0 due to us allocating s->len + 1 bytes and because uiRealloc() always zero-fills allocated memory +// this works (and returns true, which is what we want) at s->len too because s->s[s->len] is always going to be 0 due to us allocating s->len + 1 bytes and because uiprivRealloc() always zero-fills allocated memory static int onCodepointBoundary(uiAttributedString *s, size_t at) { uint8_t c; @@ -339,7 +339,7 @@ size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t nbytes = (s->len + 1) * sizeof (size_t); *n = s->len; - out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + out = (size_t *) uiprivAlloc(nbytes, "size_t[] (uiAttributedString)"); memmove(out, s->u8tou16, nbytes); return out; } @@ -351,7 +351,7 @@ size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t nbytes = (s->u16len + 1) * sizeof (size_t); *n = s->u16len; - out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + out = (size_t *) uiprivAlloc(nbytes, "size_t[] (uiAttributedString)"); memmove(out, s->u16tou8, nbytes); return out; } diff --git a/common/attrstr.h b/common/attrstr.h index b71e1864..b0192b92 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -26,3 +26,12 @@ extern size_t uiprivAttributedStringUTF16Len(uiAttributedString *s); extern size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n); extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n); extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n); + +// per-OS graphemes.c/graphemes.cpp/graphemes.m/etc. +struct graphemes { + size_t len; + size_t *pointsToGraphemes; + size_t *graphemesToPoints; +}; +extern int uiprivGraphemesTakesUTF16(void); +extern struct graphemes *uiprivNewGraphemes(void *s, size_t len); From 9c8f6849c3580654490c5467bcb67b06e6cf514f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 11:15:18 -0500 Subject: [PATCH 375/487] Synced const-correctness in ui_attrstr.h to attrstr.c, propagated that to the necessary private functions, and added some references to testing.h. --- _future/unittest/testing.h | 4 ++++ common/attrlist.c | 2 +- common/attrstr.c | 22 +++++++++++----------- common/attrstr.h | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/_future/unittest/testing.h b/_future/unittest/testing.h index 15ea2629..c2d81690 100644 --- a/_future/unittest/testing.h +++ b/_future/unittest/testing.h @@ -43,6 +43,10 @@ extern "C" { static inline void testingprivScaffold ## name(testingT *t) { name(t); } #endif +// references: +// - https://gitlab.gnome.org/GNOME/glib/blob/master/glib/gconstructor.h +// - https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glib-compile-resources.c +// - https://msdn.microsoft.com/en-us/library/bb918180.aspx #if defined(__cplusplus) #define testingprivMkCtor(name, reg) \ static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name); diff --git a/common/attrlist.c b/common/attrlist.c index 7a85aff9..377778eb 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -598,7 +598,7 @@ void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t a = attrDeleteRange(alist, a, start, end); } -void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; uiForEach ret; diff --git a/common/attrstr.c b/common/attrstr.c index 73246651..ab3f073a 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -279,6 +279,17 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) resize(s, start + count, start16 + count16); } +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) +{ + uiprivAttrListInsertAttribute(s->attrs, spec, start, end); +} + +// LONGTERM introduce an iterator object instead? +void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +{ + uiprivAttrListForEach(s->attrs, s, f, data); +} + // TODO figure out if we should count the grapheme past the end size_t uiAttributedStringNumGraphemes(uiAttributedString *s) { @@ -303,17 +314,6 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) return pos; } -void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) -{ - uiprivAttrListInsertAttribute(s->attrs, spec, start, end); -} - -// LONGTERM introduce an iterator object instead? -void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) -{ - uiprivAttrListForEach(s->attrs, s, f, data); -} - // helpers for platform-specific code const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s) diff --git a/common/attrstr.h b/common/attrstr.h index b0192b92..32e632ed 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -18,7 +18,7 @@ extern void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *al extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute type, size_t start, size_t end); extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); -extern void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +extern void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // attrstr.c extern const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s); From 32041a2ecc770fb7cb60c6ad3bca49f0ddd94ed3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 11:25:06 -0500 Subject: [PATCH 376/487] More TODOs. --- _future/unittest/testing.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_future/unittest/testing.h b/_future/unittest/testing.h index c2d81690..550e5250 100644 --- a/_future/unittest/testing.h +++ b/_future/unittest/testing.h @@ -101,6 +101,12 @@ extern void testingTFail(testingT *t); // TODO should the defered function also have t passed to it? extern void testingTDefer(testingT *t, void (*f)(void *data), void *data); +// TODO IEEE 754 helpers +// references: +// - https://www.sourceware.org/ml/libc-alpha/2009-04/msg00005.html +// - https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +// - https://stackoverflow.com/questions/5085533/is-a-c-preprocessor-identical-to-a-c-preprocessor + // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); // see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc From 036c7c12e66dbca81e85e7e145a8126063fa9db0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 13:52:33 -0500 Subject: [PATCH 377/487] Updated CONTRIBUTING.md and moved the old OS X draw text stuff out of the way. --- CONTRIBUTING.md | 6 ++++-- darwin/{_appkit_drawtext.m => OLD__appkit_drawtext.m} | 0 darwin/{_appkit_fontmatch.m => OLD__appkit_fontmatch.m} | 0 darwin/{_old_drawtext.m => OLD__old_drawtext.m} | 0 darwin/{aat.m => OLD_aat.m} | 0 darwin/{attrstr.m => OLD_attrstr.m} | 0 darwin/{drawtext.m => OLD_drawtext.m} | 0 darwin/{fontbutton.m => OLD_fontbutton.m} | 0 darwin/{fontmatch.m => OLD_fontmatch.m} | 0 darwin/{fontstyle.h => OLD_fontstyle.h} | 0 darwin/{fonttraits.m => OLD_fonttraits.m} | 0 darwin/{fontvariation.m => OLD_fontvariation.m} | 0 darwin/{graphemes.m => OLD_graphemes.m} | 0 darwin/{opentype.m => OLD_opentype.m} | 0 14 files changed, 4 insertions(+), 2 deletions(-) rename darwin/{_appkit_drawtext.m => OLD__appkit_drawtext.m} (100%) rename darwin/{_appkit_fontmatch.m => OLD__appkit_fontmatch.m} (100%) rename darwin/{_old_drawtext.m => OLD__old_drawtext.m} (100%) rename darwin/{aat.m => OLD_aat.m} (100%) rename darwin/{attrstr.m => OLD_attrstr.m} (100%) rename darwin/{drawtext.m => OLD_drawtext.m} (100%) rename darwin/{fontbutton.m => OLD_fontbutton.m} (100%) rename darwin/{fontmatch.m => OLD_fontmatch.m} (100%) rename darwin/{fontstyle.h => OLD_fontstyle.h} (100%) rename darwin/{fonttraits.m => OLD_fonttraits.m} (100%) rename darwin/{fontvariation.m => OLD_fontvariation.m} (100%) rename darwin/{graphemes.m => OLD_graphemes.m} (100%) rename darwin/{opentype.m => OLD_opentype.m} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7147ff4e..94bd199b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,9 +30,11 @@ In the event you are unsure of something, refer to existing libui code for examp libui uses camel-case for naming, with a handful of very specific exceptions (namely GObject method names, where GObject itself enforces the naming convention). -All public API names should begin with `ui` and followed by a capital letter. All public struct names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`. +All public API names should begin with `ui` and followed by a capital letter. All public struct field names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`. -No private API name should begin with `ui` followed by a capital letter; in fact, I would ideally like to be able to get away with lax naming for private functions. I may have to change that if it becomes technically difficult to do in the case of static linking later on down the road. (`uiMalloc()`, `uiRealloc()`, and `uiFree()` are grandfathered-in exceptions.) +Private API names — specifcally those used by more than one source file — should begin with `uipriv` and be followed by a capital letter. This avoids namespace collisions in static libraries. + +Static functions and static objects do not have naming restrictions. Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case name, `HTTP` for all else, but **NEVER** `Http`. This is possibly the only aspect of the controversial nature of code style that I consider indefensibly stupid. diff --git a/darwin/_appkit_drawtext.m b/darwin/OLD__appkit_drawtext.m similarity index 100% rename from darwin/_appkit_drawtext.m rename to darwin/OLD__appkit_drawtext.m diff --git a/darwin/_appkit_fontmatch.m b/darwin/OLD__appkit_fontmatch.m similarity index 100% rename from darwin/_appkit_fontmatch.m rename to darwin/OLD__appkit_fontmatch.m diff --git a/darwin/_old_drawtext.m b/darwin/OLD__old_drawtext.m similarity index 100% rename from darwin/_old_drawtext.m rename to darwin/OLD__old_drawtext.m diff --git a/darwin/aat.m b/darwin/OLD_aat.m similarity index 100% rename from darwin/aat.m rename to darwin/OLD_aat.m diff --git a/darwin/attrstr.m b/darwin/OLD_attrstr.m similarity index 100% rename from darwin/attrstr.m rename to darwin/OLD_attrstr.m diff --git a/darwin/drawtext.m b/darwin/OLD_drawtext.m similarity index 100% rename from darwin/drawtext.m rename to darwin/OLD_drawtext.m diff --git a/darwin/fontbutton.m b/darwin/OLD_fontbutton.m similarity index 100% rename from darwin/fontbutton.m rename to darwin/OLD_fontbutton.m diff --git a/darwin/fontmatch.m b/darwin/OLD_fontmatch.m similarity index 100% rename from darwin/fontmatch.m rename to darwin/OLD_fontmatch.m diff --git a/darwin/fontstyle.h b/darwin/OLD_fontstyle.h similarity index 100% rename from darwin/fontstyle.h rename to darwin/OLD_fontstyle.h diff --git a/darwin/fonttraits.m b/darwin/OLD_fonttraits.m similarity index 100% rename from darwin/fonttraits.m rename to darwin/OLD_fonttraits.m diff --git a/darwin/fontvariation.m b/darwin/OLD_fontvariation.m similarity index 100% rename from darwin/fontvariation.m rename to darwin/OLD_fontvariation.m diff --git a/darwin/graphemes.m b/darwin/OLD_graphemes.m similarity index 100% rename from darwin/graphemes.m rename to darwin/OLD_graphemes.m diff --git a/darwin/opentype.m b/darwin/OLD_opentype.m similarity index 100% rename from darwin/opentype.m rename to darwin/OLD_opentype.m From 7fd012418dd22c9ed5d90a8fc069b963b69694a8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 15:09:27 -0500 Subject: [PATCH 378/487] Migrated (and cleaned up) OS X opentype.m. --- darwin/OLD_attrstr.h | 26 ++++++ darwin/OLD_opentype.m | 191 ----------------------------------------- darwin/attrstr.h | 4 + darwin/opentype.m | 112 ++++++++++++++++++++++++ darwin/uipriv_darwin.h | 26 ------ 5 files changed, 142 insertions(+), 217 deletions(-) create mode 100644 darwin/OLD_attrstr.h delete mode 100644 darwin/OLD_opentype.m create mode 100644 darwin/attrstr.h create mode 100644 darwin/opentype.m diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h new file mode 100644 index 00000000..abe80cee --- /dev/null +++ b/darwin/OLD_attrstr.h @@ -0,0 +1,26 @@ +// 4 march 2018 + +// fontmatch.m +extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); +extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); + +// attrstr.m +extern void initUnderlineColors(void); +extern void uninitUnderlineColors(void); +typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); + +// aat.m +typedef void (^aatBlock)(uint16_t type, uint16_t selector); +extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); + +// opentype.m +// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this +// TODO remove x8tox32() +#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) diff --git a/darwin/OLD_opentype.m b/darwin/OLD_opentype.m deleted file mode 100644 index 890aa02a..00000000 --- a/darwin/OLD_opentype.m +++ /dev/null @@ -1,191 +0,0 @@ -// 11 may 2017 -#import "uipriv_darwin.h" - -struct uiOpenTypeFeatures { - NSMutableDictionary *tags; -}; - -uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) -{ - uiOpenTypeFeatures *otf; - - otf = uiNew(uiOpenTypeFeatures); - otf->tags = [NSMutableDictionary new]; - return otf; -} - -void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) -{ - [otf->tags release]; - uiFree(otf); -} - -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) -{ - uiOpenTypeFeatures *out; - - out = uiNew(uiOpenTypeFeatures); - out->tags = [otf->tags mutableCopy]; - return out; -} - -// why are there no NSNumber methods for stdint.h or the equivalent core foundation types?... -#define mkMapObject(tag) [NSNumber numberWithUnsignedLongLong:((unsigned long long) tag)] -#define mapObjectValue(num) ((uint32_t) [num unsignedLongLongValue]) - -void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) -{ - NSNumber *tn, *vn; - - tn = mkMapObject(mkTag(a, b, c, d)); - vn = mkMapObject(value); - [otf->tags setObject:vn forKey:tn]; -} - -void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) -{ - NSNumber *tn; - - tn = mkMapObject(mkTag(a, b, c, d)); - // documented as doing nothing if tn is not in otf->tags - [otf->tags removeObjectForKey:tn]; -} - -// TODO will the const wreck stuff up? -int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) -{ - NSNumber *tn, *vn; - - tn = mkMapObject(mkTag(a, b, c, d)); - vn = (NSNumber *) [otf->tags objectForKey:tn]; - if (vn == nil) - return 0; - *value = mapObjectValue(vn); - // TODO release vn? - return 1; -} - -void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) -{ - [otf->tags enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { - NSNumber *tn = (NSNumber *) key; - NSNumber *vn = (NSNumber *) value; - uint32_t tag; - uint8_t a, b, c, d; - uiForEach ret; - - tag = mapObjectValue(tn); - a = (uint8_t) ((tag >> 24) & 0xFF); - b = (uint8_t) ((tag >> 16) & 0xFF); - c = (uint8_t) ((tag >> 8) & 0xFF); - d = (uint8_t) (tag & 0xFF); - ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, - mapObjectValue(vn), data); - // TODO for all: require exact match? - if (ret == uiForEachStop) - *stop = YES; - }]; -} - -int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) -{ - if (a == NULL && b == NULL) - return 1; - if (a == NULL || b == NULL) - return 0; - return [a->tags isEqualToDictionary:b->tags]; -} - -// TODO explain all this -// TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) -// TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) -{ - CFMutableArrayRef outerArray = (CFMutableArrayRef) data; - - openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { - CFDictionaryRef innerDict; - CFNumberRef numType, numSelector; - // not well documented, but fixed-size arrays don't support __block either (VLAs are documented as being unsupported) - const void *keys[2], *values[2]; - - keys[0] = kCTFontFeatureTypeIdentifierKey; - keys[1] = kCTFontFeatureSelectorIdentifierKey; - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&type)); - numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&selector)); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); - }); - return uiForEachContinue; -} - -// TODO find out which fonts differ in AAT small caps and test them with this -static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) -{ - CFMutableArrayRef outerArray = (CFMutableArrayRef) data; - CFDictionaryRef innerDict; - // TODO rename this to tagstr (and all the other variables likewise...) - CFStringRef strTag; - CFNumberRef numValue; - char tagcstr[5]; - const void *keys[2], *values[2]; - - tagcstr[0] = a; - tagcstr[1] = b; - tagcstr[2] = c; - tagcstr[3] = d; - tagcstr[4] = '\0'; - keys[0] = *FUTURE_kCTFontOpenTypeFeatureTag; - keys[1] = *FUTURE_kCTFontOpenTypeFeatureValue; - strTag = CFStringCreateWithCString(NULL, tagcstr, kCFStringEncodingUTF8); - if (strTag == NULL) { - // TODO - } - numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, - (const SInt32 *) (&value)); - values[0] = strTag; - values[1] = numValue; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numValue); - CFRelease(strTag); - return uiForEachContinue; -} - -CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) -{ - CFMutableArrayRef outerArray; - uiOpenTypeFeaturesForEachFunc f; - - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) { - // TODO - } - f = otfArrayForEachAAT; - if (FUTURE_kCTFontOpenTypeFeatureTag != NULL && FUTURE_kCTFontOpenTypeFeatureValue != NULL) - f = otfArrayForEachOT; - uiOpenTypeFeaturesForEach(otf, f, outerArray); - return outerArray; -} diff --git a/darwin/attrstr.h b/darwin/attrstr.h new file mode 100644 index 00000000..8221b8b4 --- /dev/null +++ b/darwin/attrstr.h @@ -0,0 +1,4 @@ +// 4 march 2018 + +// opentype.m +extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf); diff --git a/darwin/opentype.m b/darwin/opentype.m new file mode 100644 index 00000000..0b2647e9 --- /dev/null +++ b/darwin/opentype.m @@ -0,0 +1,112 @@ +// 11 may 2017 +#import "uipriv_darwin.h" + +struct addCTFeatureEntryParams { + CFMutableArrayRef array; + const void *tagKey; + BOOL tagIsNumber; + CFNumberType tagType; + const void *tagValue; + const void *valueKey; + CFNumberType valueType; + const void *valueValue; +}; + +static void addCTFeatureEntry(struct addCTFeatureEntryParams *p) +{ + CFDictionaryRef featureDict; + CFNumberRef tagNum, valueNum; + const void *keys[2], *values[2]; + + keys[0] = p->tagKey; + tagNum = NULL; + values[0] = p->tagValue; + if (p->tagIsNumber) { + tagNum = CFNumberCreate(NULL, p->tagType, p->tagValue); + values[0] = tagNum; + } + + keys[1] = p->valueKey; + valueNum = CFNumberCreate(NULL, p->valueType, p->valueValue); + values[1] = valueNum; + + featureDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (featureDict == NULL) { + // TODO + } + CFArrayAppendValue(p->array, featureDict); + + CFRelease(featureDict); + CFRelease(valueNum); + if (p->tagIsNumber) + CFRelease(tagNum); +} + +static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + __block struct addCTFeatureEntryParams p; + + p.array = (CFMutableArrayRef) data; + p.tagIsNumber = YES; + openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { + p.tagKey = kCTFontFeatureTypeIdentifierKey; + p.tagType = kCFNumberSInt16Type; + p.tagValue = (const SInt16 *) (&type); + p.valueKey = kCTFontFeatureSelectorIdentifierKey; + p.valueType = kCFNumberSInt16Type; + p.valueValue = (const SInt16 *) (&selector); + addCTFeatureEntry(&p); + }); + return uiForEachContinue; +} + +// TODO find out which fonts differ in AAT small caps and test them with this +static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + struct addCTFeatureEntryParams p; + char tagcstr[5]; + CFStringRef tagstr; + + p.array = (CFMutableArrayRef) data; + + p.tagKey = *FUTURE_kCTFontOpenTypeFeatureTag; + p.tagIsNumber = NO; + tagcstr[0] = a; + tagcstr[1] = b; + tagcstr[2] = c; + tagcstr[3] = d; + tagcstr[4] = '\0'; + tagstr = CFStringCreateWithCString(NULL, tagcstr, kCFStringEncodingUTF8); + if (tagstr == NULL) { + // TODO + } + p.tagValue = tagstr; + + p.valueKey = *FUTURE_kCTFontOpenTypeFeatureValue; + p.valueType = kCFNumberSInt32Type; + p.valueValue = (const SInt32 *) (&value); + addCTFeatureEntry(&p); + + CFRelease(strTag); + return uiForEachContinue; +} + +CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf) +{ + CFMutableArrayRef array; + uiOpenTypeFeaturesForEachFunc f; + + array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (array == NULL) { + // TODO + } + f = otfArrayForEachAAT; + if (FUTURE_kCTFontOpenTypeFeatureTag != NULL && FUTURE_kCTFontOpenTypeFeatureValue != NULL) + f = otfArrayForEachOT; + uiOpenTypeFeaturesForEach(otf, f, array); + return array; +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0303c32c..4d5ebdc3 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -142,32 +142,6 @@ extern NSImage *imageImage(uiImage *); extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); -// fontmatch.m -extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); -extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); - -// attrstr.m -extern void initUnderlineColors(void); -extern void uninitUnderlineColors(void); -typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); -extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); - -// aat.m -typedef void (^aatBlock)(uint16_t type, uint16_t selector); -extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); - -// opentype.m -// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this -// TODO remove x8tox32() -#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) -#define mkTag(a, b, c, d) \ - ((x8tox32(a) << 24) | \ - (x8tox32(b) << 16) | \ - (x8tox32(c) << 8) | \ - x8tox32(d)) -extern CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf); - // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; From 36567cc52235781a31f5ab3767b241c8d9e545e7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 15:48:45 -0500 Subject: [PATCH 379/487] Migrated OS X graphemes.m. --- darwin/attrstr.h | 1 + darwin/{OLD_graphemes.m => graphemes.m} | 11 ++++++----- darwin/opentype.m | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) rename darwin/{OLD_graphemes.m => graphemes.m} (79%) diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 8221b8b4..537243bb 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -1,4 +1,5 @@ // 4 march 2018 +#import "../common/attrstr.h" // opentype.m extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf); diff --git a/darwin/OLD_graphemes.m b/darwin/graphemes.m similarity index 79% rename from darwin/OLD_graphemes.m rename to darwin/graphemes.m index e5ee816f..3fdbc17e 100644 --- a/darwin/OLD_graphemes.m +++ b/darwin/graphemes.m @@ -1,15 +1,16 @@ // 3 december 2016 #import "uipriv_darwin.h" +#import "attrstr.h" // CFStringGetRangeOfComposedCharactersAtIndex() is the function for grapheme clusters // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway -int graphemesTakesUTF16(void) +int uiprivGraphemesTakesUTF16(void) { return 1; } -struct graphemes *graphemes(void *s, size_t len) +struct graphemes *uiprivNewGraphemes(void *s, size_t len) { struct graphemes *g; UniChar *str = (UniChar *) s; @@ -18,7 +19,7 @@ struct graphemes *graphemes(void *s, size_t len) CFRange range; size_t i; - g = uiNew(struct graphemes); + g = uiprivNew(struct graphemes); cfstr = CFStringCreateWithCharactersNoCopy(NULL, str, len, kCFAllocatorNull); if (cfstr == NULL) { @@ -34,8 +35,8 @@ struct graphemes *graphemes(void *s, size_t len) ppos = range.location + range.length; } - g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->pointsToGraphemes = (size_t *) uiprivAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiprivAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); // now calculate everything // fortunately due to the use of CFRange we can do this in one loop trivially! diff --git a/darwin/opentype.m b/darwin/opentype.m index 0b2647e9..de4bb093 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -1,5 +1,6 @@ // 11 may 2017 #import "uipriv_darwin.h" +#import "attrstr.h" struct addCTFeatureEntryParams { CFMutableArrayRef array; From d8ad3300c90964961ddc7a1d915b8998c4ee15ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 15:53:46 -0500 Subject: [PATCH 380/487] Migrated aat.m back. --- darwin/OLD_attrstr.h | 14 -------------- darwin/{OLD_aat.m => aat.m} | 14 ++++++++++++-- darwin/attrstr.h | 4 ++++ darwin/opentype.m | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) rename darwin/{OLD_aat.m => aat.m} (96%) diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h index abe80cee..2a637b84 100644 --- a/darwin/OLD_attrstr.h +++ b/darwin/OLD_attrstr.h @@ -10,17 +10,3 @@ extern void initUnderlineColors(void); extern void uninitUnderlineColors(void); typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); - -// aat.m -typedef void (^aatBlock)(uint16_t type, uint16_t selector); -extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); - -// opentype.m -// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this -// TODO remove x8tox32() -#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) -#define mkTag(a, b, c, d) \ - ((x8tox32(a) << 24) | \ - (x8tox32(b) << 16) | \ - (x8tox32(c) << 8) | \ - x8tox32(d)) diff --git a/darwin/OLD_aat.m b/darwin/aat.m similarity index 96% rename from darwin/OLD_aat.m rename to darwin/aat.m index 0c3fd5fa..05f0b268 100644 --- a/darwin/OLD_aat.m +++ b/darwin/aat.m @@ -1,9 +1,10 @@ // 14 february 2017 #import "uipriv_darwin.h" +#import "attrstr.h" // TODO explain the purpose of this file -static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, aatBlock f) +static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, uiprivAATBlock f) { // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) if (value != 0) { @@ -13,8 +14,17 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if f(type, ifFalse); } +// TODO remove the need for this +// TODO remove x8tox32() +#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) + // TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) -void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) +void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, uiprivAATBlock f) { switch (mkTag(a, b, c, d)) { case mkTag('l', 'i', 'g', 'a'): diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 537243bb..74be3a37 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -3,3 +3,7 @@ // opentype.m extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf); + +// aat.m +typedef void (^uiprivAATBlock)(uint16_t type, uint16_t selector); +extern void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, uiprivAATBlock f); diff --git a/darwin/opentype.m b/darwin/opentype.m index de4bb093..079197ed 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -53,7 +53,7 @@ static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char p.array = (CFMutableArrayRef) data; p.tagIsNumber = YES; - openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { + uiprivOpenTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { p.tagKey = kCTFontFeatureTypeIdentifierKey; p.tagType = kCFNumberSInt16Type; p.tagValue = (const SInt16 *) (&type); From 0b3176cead9471ef77baae90da411e5be07afb53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 18:01:08 -0500 Subject: [PATCH 381/487] Migrated the font matchng stuff on OS X. --- darwin/OLD_attrstr.h | 5 -- darwin/OLD_fontstyle.h | 61 ------------------ darwin/attrstr.h | 63 +++++++++++++++++++ darwin/{OLD_fontmatch.m => fontmatch.m} | 34 +++++----- darwin/{OLD_fonttraits.m => fonttraits.m} | 14 ++--- .../{OLD_fontvariation.m => fontvariation.m} | 7 ++- 6 files changed, 91 insertions(+), 93 deletions(-) delete mode 100644 darwin/OLD_fontstyle.h rename darwin/{OLD_fontmatch.m => fontmatch.m} (92%) rename darwin/{OLD_fonttraits.m => fonttraits.m} (92%) rename darwin/{OLD_fontvariation.m => fontvariation.m} (96%) diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h index 2a637b84..49f5fabc 100644 --- a/darwin/OLD_attrstr.h +++ b/darwin/OLD_attrstr.h @@ -1,10 +1,5 @@ // 4 march 2018 -// fontmatch.m -extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); -extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); - // attrstr.m extern void initUnderlineColors(void); extern void uninitUnderlineColors(void); diff --git a/darwin/OLD_fontstyle.h b/darwin/OLD_fontstyle.h deleted file mode 100644 index 8a27380c..00000000 --- a/darwin/OLD_fontstyle.h +++ /dev/null @@ -1,61 +0,0 @@ -// 3 november 2017 - -// fontmatch.m -@interface fontStyleData : NSObject { - CTFontRef font; - CTFontDescriptorRef desc; - CFDictionaryRef traits; - CTFontSymbolicTraits symbolic; - double weight; - double width; - BOOL didStyleName; - CFStringRef styleName; - BOOL didVariation; - CFDictionaryRef variation; - BOOL hasRegistrationScope; - CTFontManagerScope registrationScope; - BOOL didPostScriptName; - CFStringRef postScriptName; - CTFontFormat fontFormat; - BOOL didPreferredSubFamilyName; - CFStringRef preferredSubFamilyName; - BOOL didSubFamilyName; - CFStringRef subFamilyName; - BOOL didFullName; - CFStringRef fullName; - BOOL didPreferredFamilyName; - CFStringRef preferredFamilyName; - BOOL didFamilyName; - CFStringRef familyName; - BOOL didVariationAxes; - CFArrayRef variationAxes; -} -- (id)initWithFont:(CTFontRef)f; -- (id)initWithDescriptor:(CTFontDescriptorRef)d; -- (BOOL)prepare; -- (void)ensureFont; -- (CTFontSymbolicTraits)symbolicTraits; -- (double)weight; -- (double)width; -- (CFStringRef)styleName; -- (CFDictionaryRef)variation; -- (BOOL)hasRegistrationScope; -- (CTFontManagerScope)registrationScope; -- (CFStringRef)postScriptName; -- (CFDataRef)table:(CTFontTableTag)tag; -- (CTFontFormat)fontFormat; -- (CFStringRef)fontName:(CFStringRef)key; -- (CFStringRef)preferredSubFamilyName; -- (CFStringRef)subFamilyName; -- (CFStringRef)fullName; -- (CFStringRef)preferredFamilyName; -- (CFStringRef)familyName; -- (CFArrayRef)variationAxes; -@end - -// fonttraits.m -extern void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out); - -// fontvariation.m -extern NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); -extern void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 74be3a37..7b1dbb74 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -7,3 +7,66 @@ extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *o // aat.m typedef void (^uiprivAATBlock)(uint16_t type, uint16_t selector); extern void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, uiprivAATBlock f); + +// fontmatch.m +@interface uiprivFontStyleData : NSObject { + CTFontRef font; + CTFontDescriptorRef desc; + CFDictionaryRef traits; + CTFontSymbolicTraits symbolic; + double weight; + double width; + BOOL didStyleName; + CFStringRef styleName; + BOOL didVariation; + CFDictionaryRef variation; + BOOL hasRegistrationScope; + CTFontManagerScope registrationScope; + BOOL didPostScriptName; + CFStringRef postScriptName; + CTFontFormat fontFormat; + BOOL didPreferredSubFamilyName; + CFStringRef preferredSubFamilyName; + BOOL didSubFamilyName; + CFStringRef subFamilyName; + BOOL didFullName; + CFStringRef fullName; + BOOL didPreferredFamilyName; + CFStringRef preferredFamilyName; + BOOL didFamilyName; + CFStringRef familyName; + BOOL didVariationAxes; + CFArrayRef variationAxes; +} +- (id)initWithFont:(CTFontRef)f; +- (id)initWithDescriptor:(CTFontDescriptorRef)d; +- (BOOL)prepare; +- (void)ensureFont; +- (CTFontSymbolicTraits)symbolicTraits; +- (double)weight; +- (double)width; +- (CFStringRef)styleName; +- (CFDictionaryRef)variation; +- (BOOL)hasRegistrationScope; +- (CTFontManagerScope)registrationScope; +- (CFStringRef)postScriptName; +- (CFDataRef)table:(CTFontTableTag)tag; +- (CTFontFormat)fontFormat; +- (CFStringRef)fontName:(CFStringRef)key; +- (CFStringRef)preferredSubFamilyName; +- (CFStringRef)subFamilyName; +- (CFStringRef)fullName; +- (CFStringRef)preferredFamilyName; +- (CFStringRef)familyName; +- (CFArrayRef)variationAxes; +@end +extern CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); +extern void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); + +// fonttraits.m +extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out); + +// fontvariation.m +extern NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); +extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); diff --git a/darwin/OLD_fontmatch.m b/darwin/fontmatch.m similarity index 92% rename from darwin/OLD_fontmatch.m rename to darwin/fontmatch.m index 25476900..6a9eb14e 100644 --- a/darwin/OLD_fontmatch.m +++ b/darwin/fontmatch.m @@ -1,6 +1,6 @@ // 3 january 2017 #import "uipriv_darwin.h" -#import "fontstyle.h" +#import "attrstr.h" // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature @@ -26,7 +26,7 @@ // because the descriptors returned by Core Text's own font // matching won't have any. -@implementation fontStyleData +@implementation uiprivFontStyleData - (id)initWithFont:(CTFontRef)f { @@ -294,7 +294,7 @@ static const double *italicClosenesses[] = { // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextItalic guessItalicOblique(fontStyleData *d) +static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) { CFStringRef styleName; BOOL isOblique; @@ -318,20 +318,20 @@ static uiDrawTextItalic guessItalicOblique(fontStyleData *d) // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static void setItalic(fontStyleData *d, uiDrawFontDescriptor *out) +static void setItalic(uiprivFontStyleData *d, uiDrawFontDescriptor *out) { out->Italic = uiDrawTextItalicNormal; if (([d symbolicTraits] & kCTFontItalicTrait) != 0) out->Italic = guessItalicOblique(d); } -static void fillDescStyleFields(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) { setItalic(d, out); if (axisDict != nil) - processFontVariation(d, axisDict, out); + uiprivProcessFontVariation(d, axisDict, out); else - processFontTraits(d, out); + uiprivProcessFontTraits(d, out); } static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDescriptor *styles) @@ -341,7 +341,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes struct closeness *closeness; CTFontDescriptorRef current; CTFontDescriptorRef out; - fontStyleData *d; + uiprivFontStyleData *d; NSDictionary *axisDict; matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); @@ -356,10 +356,10 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes } current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, 0); - d = [[fontStyleData alloc] initWithDescriptor:current]; + d = [[uiprivFontStyleData alloc] initWithDescriptor:current]; axisDict = nil; if ([d variation] != NULL) - axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); + axisDict = uiprivMakeVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { @@ -368,7 +368,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes closeness[i].index = i; if (i != 0) { current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); - d = [[fontStyleData alloc] initWithDescriptor:current]; + d = [[uiprivFontStyleData alloc] initWithDescriptor:current]; } fillDescStyleFields(d, axisDict, &fields); closeness[i].weight = fields.Weight - styles->Weight; @@ -413,7 +413,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes return out; } -CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd) { CFMutableDictionaryRef attrs; CFStringRef cffamily; @@ -443,7 +443,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) } // fortunately features that aren't supported are simply ignored, so we can copy them all in -CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) +CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; CFArrayRef featuresArray; @@ -465,10 +465,10 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpe return new; } -void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) +void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) { CFStringRef cffamily; - fontStyleData *d; + uiprivFontStyleData *d; NSDictionary *axisDict; cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontFamilyNameAttribute); @@ -479,10 +479,10 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - d = [[fontStyleData alloc] initWithDescriptor:ctdesc]; + d = [[uiprivFontStyleData alloc] initWithDescriptor:ctdesc]; axisDict = nil; if ([d variation] != NULL) - axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); + axisDict = uiprivMakeVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); fillDescStyleFields(d, axisDict, uidesc); if (axisDict != nil) [axisDict release]; diff --git a/darwin/OLD_fonttraits.m b/darwin/fonttraits.m similarity index 92% rename from darwin/OLD_fonttraits.m rename to darwin/fonttraits.m index feb37b8b..b4b932c8 100644 --- a/darwin/OLD_fonttraits.m +++ b/darwin/fonttraits.m @@ -1,6 +1,6 @@ // 1 november 2017 #import "uipriv_darwin.h" -#import "fontstyle.h" +#import "attrstr.h" // This is the part of the font style matching and normalization code // that handles fonts that use a traits dictionary. @@ -19,7 +19,7 @@ // what. We'll just convert Core Text's values into libui constants // and use those for matching. -static BOOL fontRegistered(fontStyleData *d) +static BOOL fontRegistered(uiprivFontStyleData *d) { if (![d hasRegistrationScope]) // header says this should be treated as the same as not registered @@ -54,7 +54,7 @@ static const CFStringRef exceptions[] = { NULL, }; -static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +static void trySecondaryOS2Values(uiprivFontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) { CFDataRef os2; uint16_t usWeightClass, usWidthClass; @@ -125,7 +125,7 @@ static BOOL testTTFOTFSubfamilyName(CFStringRef name, CFStringRef want) (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false; } -static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) +static BOOL testTTFOTFSubfamilyNames(uiprivFontStyleData *d, CFStringRef want) { switch ([d fontFormat]) { case kCTFontFormatOpenTypePostScript: @@ -148,18 +148,18 @@ static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeThin(fontStyleData *d) +static BOOL shouldReallyBeThin(uiprivFontStyleData *d) { return testTTFOTFSubfamilyNames(d, CFSTR("W1")); } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeSemiCondensed(fontStyleData *d) +static BOOL shouldReallyBeSemiCondensed(uiprivFontStyleData *d) { return testTTFOTFSubfamilyNames(d, CFSTR("Semi Condensed")); } -void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) +void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out) { double weight, width; BOOL hasWeight, hasWidth; diff --git a/darwin/OLD_fontvariation.m b/darwin/fontvariation.m similarity index 96% rename from darwin/OLD_fontvariation.m rename to darwin/fontvariation.m index 3e22c710..9959b02b 100644 --- a/darwin/OLD_fontvariation.m +++ b/darwin/fontvariation.m @@ -1,6 +1,6 @@ // 2 november 2017 #import "uipriv_darwin.h" -#import "fontstyle.h" +#import "attrstr.h" // This is the part of the font style matching and normalization code // that handles fonts that use the fvar table. @@ -205,6 +205,7 @@ static BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed161 return YES; } +// TODO here and elsewhere: make sure all Objective-C classes and possibly also custom method names have uipriv prefixes @interface fvarAxis : NSObject { fixed1616 min; fixed1616 max; @@ -262,7 +263,7 @@ fail: @end -NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) +NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) { CFDictionaryRef axis; CFIndex i, n; @@ -304,7 +305,7 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, return YES; } -void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) { CFDictionaryRef var; double v; From 1fc9f137bc7ad54333e549f9db3872710c711752 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 19:51:45 -0500 Subject: [PATCH 382/487] Migrated fontbutton.m back. --- darwin/{OLD_fontbutton.m => fontbutton.m} | 27 ++++++++++++----------- darwin/main.m | 6 ++--- darwin/uipriv_darwin.h | 6 ++--- 3 files changed, 20 insertions(+), 19 deletions(-) rename darwin/{OLD_fontbutton.m => fontbutton.m} (87%) diff --git a/darwin/OLD_fontbutton.m b/darwin/fontbutton.m similarity index 87% rename from darwin/OLD_fontbutton.m rename to darwin/fontbutton.m index 433f261f..38205135 100644 --- a/darwin/OLD_fontbutton.m +++ b/darwin/fontbutton.m @@ -1,7 +1,8 @@ // 14 april 2016 #import "uipriv_darwin.h" +#import "attrstr.h" -@interface fontButton : NSButton { +@interface uiprivFontButton : NSButton { uiFontButton *libui_b; NSFont *libui_font; } @@ -15,16 +16,16 @@ @end // only one may be active at one time -static fontButton *activeFontButton = nil; +static uiprivFontButton *activeFontButton = nil; struct uiFontButton { uiDarwinControl c; - fontButton *button; + uiprivFontButton *button; void (*onChanged)(uiFontButton *, void *); void *onChangedData; }; -@implementation fontButton +@implementation uiprivFontButton - (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b { @@ -145,7 +146,7 @@ struct uiFontButton { ctfont = (CTFontRef) (self->libui_font); ctdesc = CTFontCopyFontDescriptor(ctfont); - fontdescFromCTFontDescriptor(ctdesc, uidesc); + uiprivDrawFontDescriptorFromCTFontDescriptor(ctdesc, uidesc); CFRelease(ctdesc); uidesc->Size = CTFontGetSize(ctfont); } @@ -156,16 +157,16 @@ uiDarwinControlAllDefaults(uiFontButton, button) // we do not want font change events to be sent to any controls other than the font buttons // see main.m for more details -BOOL fontButtonInhibitSendAction(SEL sel, id from, id to) +BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to) { if (sel != @selector(changeFont:)) return NO; - return ![to isKindOfClass:[fontButton class]]; + return ![to isKindOfClass:[uiprivFontButton class]]; } // we do not want NSFontPanelValidation messages to be sent to any controls other than the font buttons when a font button is active // see main.m for more details -BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) +BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) { if (activeFontButton == nil) return NO; @@ -177,10 +178,10 @@ BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) // we also don't want the panel to be usable when there's a dialog running; see stddialogs.m for more details on that // unfortunately the panel seems to ignore -setWorksWhenModal: so we'll have to do things ourselves -@interface nonModalFontPanel : NSFontPanel +@interface uiprivNonModalFontPanel : NSFontPanel @end -@implementation nonModalFontPanel +@implementation uiprivNonModalFontPanel - (BOOL)worksWhenModal { @@ -189,9 +190,9 @@ BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) @end -void setupFontPanel(void) +void uiprivSetupFontPanel(void) { - [NSFontManager setFontPanelFactory:[nonModalFontPanel class]]; + [NSFontManager setFontPanelFactory:[uiprivNonModalFontPanel class]]; } static void defaultOnChanged(uiFontButton *b, void *data) @@ -216,7 +217,7 @@ uiFontButton *uiNewFontButton(void) uiDarwinNewControl(uiFontButton, b); - b->button = [[fontButton alloc] initWithFrame:NSZeroRect libuiFontButton:b]; + b->button = [[uiprivFontButton alloc] initWithFrame:NSZeroRect libuiFontButton:b]; uiDarwinSetControlFont(b->button, NSRegularControlSize); uiFontButtonOnChanged(b, defaultOnChanged, NULL); diff --git a/darwin/main.m b/darwin/main.m index d85000dd..12c528ff 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -26,7 +26,7 @@ static BOOL stepsIsRunning; { if (colorButtonInhibitSendAction(sel, from, to)) return NO; - if (fontButtonInhibitSendAction(sel, from, to)) + if (uiprivFontButtonInhibitSendAction(sel, from, to)) return NO; return [super sendAction:sel to:to from:from]; } @@ -38,7 +38,7 @@ static BOOL stepsIsRunning; { id override; - if (fontButtonOverrideTargetForAction(sel, from, to, &override)) + if (uiprivFontButtonOverrideTargetForAction(sel, from, to, &override)) return override; return [super targetForAction:sel to:to from:from]; } @@ -126,7 +126,7 @@ const char *uiInit(uiInitOptions *o) appDelegate().menuManager = [[menuManager new] autorelease]; [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; - setupFontPanel(); + uiprivSetupFontPanel(); initUnderlineColors(); } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 4d5ebdc3..0711c68b 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -111,9 +111,9 @@ extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); // fontbutton.m -extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); -extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); -extern void setupFontPanel(void); +extern BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to); +extern BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); +extern void uiprivSetupFontPanel(void); // colorbutton.m extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); From 232b14ccde057c1cb22931c4fce196206540ab44 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 20:01:52 -0500 Subject: [PATCH 383/487] Migrated attrstr.m. This file needs to be cleaned up... --- darwin/OLD_attrstr.h | 7 ---- darwin/attrstr.h | 6 ++++ darwin/{OLD_attrstr.m => attrstr.m} | 50 ++++++++++++++++------------- 3 files changed, 33 insertions(+), 30 deletions(-) delete mode 100644 darwin/OLD_attrstr.h rename darwin/{OLD_attrstr.m => attrstr.m} (89%) diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h deleted file mode 100644 index 49f5fabc..00000000 --- a/darwin/OLD_attrstr.h +++ /dev/null @@ -1,7 +0,0 @@ -// 4 march 2018 - -// attrstr.m -extern void initUnderlineColors(void); -extern void uninitUnderlineColors(void); -typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); -extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 7b1dbb74..14a1cbe5 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -70,3 +70,9 @@ extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor // fontvariation.m extern NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); + +// attrstr.m +extern void uiprivInitUnderlineColors(void); +extern void uiprivUninitUnderlineColors(void); +typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/darwin/OLD_attrstr.m b/darwin/attrstr.m similarity index 89% rename from darwin/OLD_attrstr.m rename to darwin/attrstr.m index fd45ec25..4d20bc37 100644 --- a/darwin/OLD_attrstr.m +++ b/darwin/attrstr.m @@ -1,5 +1,6 @@ // 12 february 2017 #import "uipriv_darwin.h" +#import "attrstr.h" // this is what AppKit does internally // WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -17,7 +18,7 @@ static NSColor *tryColorNamed(NSString *name) return [NSColor colorWithPatternImage:img]; } -void initUnderlineColors(void) +void uiprivInitUnderlineColors(void) { spellingColor = tryColorNamed(@"NSSpellingDot"); if (spellingColor == nil) { @@ -47,11 +48,14 @@ void initUnderlineColors(void) [auxiliaryColor retain]; // override autoreleasing } -void uninitUnderlineColors(void) +void uiprivUninitUnderlineColors(void) { [auxiliaryColor release]; + auxiliaryColor = nil; [grammarColor release]; + grammarColors = nil; [spellingColor release]; + spellingColor = nil; } // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute @@ -62,12 +66,12 @@ void uninitUnderlineColors(void) // TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are combinedFontAttr objects + NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are uiprivCombinedFontAttr objects uiDrawFontDescriptor *defaultFont; NSMutableArray *backgroundBlocks; }; -@interface combinedFontAttr : NSObject +@interface uiprivCombinedFontAttr : NSObject @property const char *family; @property double size; @property uiDrawTextWeight weight; @@ -75,11 +79,11 @@ struct foreachParams { @property uiDrawTextStretch stretch; @property const uiOpenTypeFeatures *features; - (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; -- (BOOL)same:(combinedFontAttr *)b; +- (BOOL)same:(uiprivCombinedFontAttr *)b; - (CTFontRef)toCTFont; @end -@implementation combinedFontAttr +@implementation uiprivCombinedFontAttr - (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont { @@ -97,7 +101,7 @@ struct foreachParams { } // TODO deduplicate this with common/attrlist.c -- (BOOL)same:(combinedFontAttr *)b +- (BOOL)same:(uiprivCombinedFontAttr *)b { // TODO should this be case-insensitive? if (strcmp(self.family, b.family) != 0) @@ -144,26 +148,26 @@ static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; NSNumber *n; - combinedFontAttr *new; + uiprivCombinedFontAttr *new; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; if ([p->combinedFontAttrs objectForKey:n] != nil) continue; - new = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + new = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; [p->combinedFontAttrs setObject:new forKey:n]; } } -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(combinedFontAttr *cfa)) +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiprivCombinedFontAttr *cfa)) { size_t i; NSNumber *n; - combinedFontAttr *cfa; + uiprivCombinedFontAttr *cfa; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - cfa = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + cfa = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; adj(cfa); } } @@ -220,31 +224,31 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.family = spec->Family; }); break; case uiAttributeSize: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.stretch = (uiDrawTextStretch) (spec->Value); }); break; @@ -300,7 +304,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; case uiAttributeFeatures: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.features = spec->Features; }); break; @@ -311,7 +315,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute return uiForEachContinue; } -static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) +static BOOL cfaIsEqual(uiprivCombinedFontAttr *a, uiprivCombinedFontAttr *b) { if (a == nil && b == nil) return YES; @@ -323,13 +327,13 @@ static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) static void applyAndFreeFontAttributes(struct foreachParams *p) { CFIndex i, n; - combinedFontAttr *cfa, *cfab; + uiprivCombinedFontAttr *cfa, *cfab; CTFontRef defaultFont; CTFontRef font; CFRange range; // first get the default font as a CTFontRef - cfa = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; defaultFont = [cfa toCTFont]; [cfa release]; @@ -344,7 +348,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) // TODO use NSValue or some other method of NSNumber nn = [NSNumber numberWithInteger:i]; - cfab = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; + cfab = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; if (cfaIsEqual(cfa, cfab)) continue; @@ -403,7 +407,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } -CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) +CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; From 49c3f77d4625d25d27aa42cca6c2f9258305bab4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 20:45:05 -0500 Subject: [PATCH 384/487] Started cleaning up attrstr.m. This is gonna be fun... --- darwin/attrstr.m | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 4d20bc37..1b2db2ea 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -59,7 +59,7 @@ void uiprivUninitUnderlineColors(void) } // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute -// TODO opentype features are lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? +// TODO opentype features are lost when using uiDrawFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? @@ -100,7 +100,7 @@ struct foreachParams { return self; } -// TODO deduplicate this with common/attrlist.c +// TODO deduplicate this with common/attribute.c - (BOOL)same:(uiprivCombinedFontAttr *)b { // TODO should this be case-insensitive? @@ -135,7 +135,7 @@ struct foreachParams { uidesc.Stretch = self.stretch; desc = fontdescToCTFontDescriptor(&uidesc); if (self.features != NULL) - desc = fontdescAppendFeatures(desc, self.features); + desc = uiprivCTFontDescriptorAppendFeatures(desc, self.features); font = CTFontCreateWithFontDescriptor(desc, self.size, NULL); CFRelease(desc); // TODO correct? return font; @@ -143,22 +143,6 @@ struct foreachParams { @end -// TODO merge this with adjustFontInRange() (and TODO figure out why they were separate in the first place; probably Windows?) -static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) -{ - size_t i; - NSNumber *n; - uiprivCombinedFontAttr *new; - - for (i = start; i < end; i++) { - n = [NSNumber numberWithInteger:i]; - if ([p->combinedFontAttrs objectForKey:n] != nil) - continue; - new = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; - [p->combinedFontAttrs setObject:new forKey:n]; - } -} - static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiprivCombinedFontAttr *cfa)) { size_t i; @@ -168,6 +152,10 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; cfa = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + if (cfa == nil) { + cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + [p->combinedFontAttrs setObject:cfa forKey:n]; + } adj(cfa); } } @@ -192,6 +180,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) CGColorRef color; CGFloat components[4]; + // TODO we should probably just create this once and recycle it throughout program execution... colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); if (colorspace == NULL) { // TODO @@ -205,7 +194,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; @@ -217,37 +206,33 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute ostart = start; oend = end; - start = attrstrUTF8ToUTF16(s, start); - end = attrstrUTF8ToUTF16(s, end); + start = uiprivAttributedStringUTF8ToUTF16(s, start); + end = uiprivAttributedStringUTF8ToUTF16(s, end); range.location = start; range.length = end - start; - switch (spec->Type) { + switch (uiAttributeGetType(attr)) { +$$TODO_CONTINUE_HERE case uiAttributeFamily: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.family = spec->Family; }); break; case uiAttributeSize: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.size = spec->Double; }); break; case uiAttributeWeight: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.stretch = (uiDrawTextStretch) (spec->Value); }); @@ -303,7 +288,6 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute CFRelease(color); break; case uiAttributeFeatures: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.features = spec->Features; }); From 9e57c78fb35051c80e1e41524a46d71536b5199f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 23:57:18 -0500 Subject: [PATCH 385/487] Started really refactoring attrstr.m by turning uiprivCombinedFontAttr into an actual attribute that CFAttributedString will manage itself; it will store uiAttributes (so we can use uiprivAttributeEqual() instead of duplicating its logic). Later, we will make it convert to CTFontRefs in place. --- darwin/attrstr.m | 147 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 47 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 1b2db2ea..7b0dc562 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -71,72 +71,125 @@ struct foreachParams { NSMutableArray *backgroundBlocks; }; -@interface uiprivCombinedFontAttr : NSObject -@property const char *family; -@property double size; -@property uiDrawTextWeight weight; -@property uiDrawTextItalic italic; -@property uiDrawTextStretch stretch; -@property const uiOpenTypeFeatures *features; -- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; -- (BOOL)same:(uiprivCombinedFontAttr *)b; -- (CTFontRef)toCTFont; +// TODO what if this is NULL? +static const CFStringRef combinedFontAttrName = CFSTR("libuiCombinedFontAttribute"); + +enum { + cFamily, + cSize, + cWeight, + cItalic, + cStretch, + cFeatures, + nc, +}; + +static const int toc[] = { + [uiAttributeFamily] = cFamily, + [uiAttributeSize] = cSize, + [uiAttributeWeight] = cWeight, + [uiAttributeItalic] = cItalic, + [uiAttributeStretch] = cStretch, + [uiAttributeFeatures] = cFeatures, +}; + +@interface uiprivCombinedFontAttr : NSObject { + uiAttribute *attrs[nc]; +} +- (void)setAttribute:(uiAttribute *)attr; +- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; @end @implementation uiprivCombinedFontAttr -- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont +- (id)init { self = [super init]; - if (self) { - // TODO define behaviors if defaultFont->Family or any attribute Family is NULL, same with other invalid values - self.family = defaultFont->Family; - self.size = defaultFont->Size; - self.weight = defaultFont->Weight; - self.italic = defaultFont->Italic; - self.stretch = defaultFont->Stretch; - self.features = NULL; - } + if (self) + memset(self->attrs, 0, nc * sizeof (uiAttribute *)); return self; } -// TODO deduplicate this with common/attribute.c -- (BOOL)same:(uiprivCombinedFontAttr *)b +- (void)dealloc { - // TODO should this be case-insensitive? - if (strcmp(self.family, b.family) != 0) - return NO; - // TODO use a closest match? - if (self.size != b.size) - return NO; - if (self.weight != b.weight) - return NO; - if (self.italic != b.italic) - return NO; - if (self.stretch != b.stretch) - return NO; - // this also handles NULL cases - if (!uiOpenTypeFeaturesEqual(self.features, b.features)) + int i; + + for (i = 0; i < nc; i++) + if (self->attrs[i] != NULL) { + uiprivAttributeRelease(self->attrs[i]); + self->attrs[i] = NULL; + } + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + uiprivCombinedFontAttr *ret; + int i; + + ret = [[uiprivCombinedFontAttr allocWithZone:zone] init]; + for (i = 0; i < nc; i++) + if (self->attrs[i] != NULL) + ret->attrs[i] = uiprivAttributeRetain(self->attrs[i]); + return ret; +} + +- (void)setAttribute:(uiAttribute *)attr +{ + int index; + + index = toc[uiAttributeGetType(attr)]; + if (self->attrs[index] != NULL) + uiprivAttributeRelease(self->attrs[index]); + self->attrs[index] = uiprivAttributeRetain(attr); +} + +- (BOOL)isEqual:(id)bb +{ + uiprivCombinedFontAttr *b = (uiprivCombinedFontAttr *) bb; + int i; + + if (b == nil) return NO; + for (i = 0; i < nc; i++) { + if (self->attrs[i] == NULL && b->attrs[i] == NULL) + continue; + if (self->attrs[i] == NULL || b->attrs[i] == NULL) + return NO; + if (!uiprivAttributeEqual(self->attrs[i], b->attrs[i])) + return NO; + } return YES; } -- (CTFontRef)toCTFont +- (NSUInteger)hash +{ + // TODO implement this somehow; not quite sure how... + return [super hash]; +} + +- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont { uiDrawFontDescriptor uidesc; CTFontDescriptorRef desc; CTFontRef font; - // TODO const-correct uiDrawFontDescriptor or change this function below - uidesc.Family = (char *) (self.family); - uidesc.Size = self.size; - uidesc.Weight = self.weight; - uidesc.Italic = self.italic; - uidesc.Stretch = self.stretch; - desc = fontdescToCTFontDescriptor(&uidesc); - if (self.features != NULL) - desc = uiprivCTFontDescriptorAppendFeatures(desc, self.features); - font = CTFontCreateWithFontDescriptor(desc, self.size, NULL); + uidesc = *defaultFont; + if (self->attrs[cFamily] != NULL) + // TODO const-correct uiDrawFontDescriptor or change this function below + uidesc.Family = (char *) uiAttributeFamily(self->attrs[cFamily]); + if (self->attrs[cSize] != NULL) + uidesc.Size = uiAttributeSize(self->attrs[cSize]); + if (self->attrs[cWeight] != NULL) + uidesc.Weight = uiAttributeWeight(self->attrs[cWeight]); + if (self->attrs[cItalic] != NULL) + uidesc.Italic = uiAttributeItalic(self->attrs[cItalic]); + if (self->attrs[cStretch] != NULL) + uidesc.Stretch = uiAttributeStretch(self->attrs[cStretch]); + desc = uiprivDrawFontDescriptorToCTFontDescriptor(&uidesc); + if (self->attrs[cFeatures] != NULL) + desc = uiprivCTFontDescriptorAppendFeatures(desc, uiAttributeFeatures(self->attrs[cFeatures])); + font = CTFontCreateWithFontDescriptor(desc, uidesc.Size, NULL); CFRelease(desc); // TODO correct? return font; } From 93c375fd940feef55e71d74b6bc66719a6c51a70 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Mar 2018 08:59:45 -0500 Subject: [PATCH 386/487] Implemented -[uiprivCombinedFontAttr hash]. --- darwin/attrstr.m | 64 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 7b0dc562..268ed00c 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -85,16 +85,32 @@ enum { }; static const int toc[] = { - [uiAttributeFamily] = cFamily, - [uiAttributeSize] = cSize, - [uiAttributeWeight] = cWeight, - [uiAttributeItalic] = cItalic, - [uiAttributeStretch] = cStretch, - [uiAttributeFeatures] = cFeatures, + [uiAttributeTypeFamily] = cFamily, + [uiAttributeTypeSize] = cSize, + [uiAttributeTypeWeight] = cWeight, + [uiAttributeTypeItalic] = cItalic, + [uiAttributeTypeStretch] = cStretch, + [uiAttributeTypeFeatures] = cFeatures, }; +static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + NSUInteger *hash = (NSUInteger *) data; + uint32_t tag; + + tag = (((uint32_t) a) & 0xFF) << 24; + tag |= (((uint32_t) b) & 0xFF) << 16; + tag |= (((uint32_t) c) & 0xFF) << 8; + tag |= ((uint32_t) d) & 0xFF; + *hash ^= tag; + *hash ^= value; + return uiForEachContinue; +} + @interface uiprivCombinedFontAttr : NSObject { uiAttribute *attrs[nc]; + BOOL hasHash; + NSUInteger hash; } - (void)setAttribute:(uiAttribute *)attr; - (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; @@ -105,8 +121,10 @@ static const int toc[] = { - (id)init { self = [super init]; - if (self) + if (self) { memset(self->attrs, 0, nc * sizeof (uiAttribute *)); + self->hasHash = NO; + } return self; } @@ -131,6 +149,8 @@ static const int toc[] = { for (i = 0; i < nc; i++) if (self->attrs[i] != NULL) ret->attrs[i] = uiprivAttributeRetain(self->attrs[i]); + ret->hasHash = self->hasHash; + ret->hash = self->hash; return ret; } @@ -142,6 +162,7 @@ static const int toc[] = { if (self->attrs[index] != NULL) uiprivAttributeRelease(self->attrs[index]); self->attrs[index] = uiprivAttributeRetain(attr); + self->hasHash = NO; } - (BOOL)isEqual:(id)bb @@ -164,8 +185,33 @@ static const int toc[] = { - (NSUInteger)hash { - // TODO implement this somehow; not quite sure how... - return [super hash]; + if (self->hasHash) + return self->hash; + @autoreleasepool { + NSString *family; + NSNumber *size; + + self->hash = 0; + if (self->attrs[cFamily] != NULL) { + family = [NSString stringWithUTF8String:uiAttributeFamily(self->attrs[cFamily])]; + // TODO make sure this aligns with case-insensitive compares when those are done in common/attribute.c + self->hash ^= [[family uppercaseString] hash]; + } + if (self->attrs[cSize] != NULL) { + size = [NSNumber numberWithDouble:uiAttributeSize(self->attrs[cSize])]; + self->hash ^= [size hash]; + } + if (self->attrs[cWeight] != NULL) + self->hash ^= (NSUInteger) uiAttributeWeight(self->attrs[cWeight]); + if (self->attrs[cItalic] != NULL) + self->hash ^= (NSUInteger) uiAttributeItalic(self->attrs[cItalic]); + if (self->attrs[cStretch] != NULL) + self->hash ^= (NSUInteger) uiAttributeStretch(self->attrs[cStretch]); + if (self->attrs[cFeatures] != NULL) + uiOpenTypeFeaturesForEach(uiAttributeFeatures(self->attrs[cFeatures]), featuresHash, &(self->hash)); + self->hasHash = YES; + } + return self->hash; } - (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont From 00749b07ac073790ebd01d36ba379afeafac282c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Mar 2018 23:15:31 -0500 Subject: [PATCH 387/487] And cleaned up all the font handling stuff in attrstr.m. A lot cleaner now! Not fully clean, but clean*er*, and probably more efficient, too... --- darwin/attrstr.m | 175 ++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 110 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 268ed00c..e2a53e0d 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -58,7 +58,6 @@ void uiprivUninitUnderlineColors(void) spellingColor = nil; } -// unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute // TODO opentype features are lost when using uiDrawFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) @@ -66,11 +65,13 @@ void uiprivUninitUnderlineColors(void) // TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are uiprivCombinedFontAttr objects - uiDrawFontDescriptor *defaultFont; NSMutableArray *backgroundBlocks; }; +// unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute +// instead of incrementally adjusting CTFontRefs (which, judging from NSFontManager, seems finicky and UI-centric), we use a custom class to incrementally store attributes that go into a CTFontRef, and then convert everything to CTFonts en masse later +// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/ChangingAttrStrings.html#//apple_ref/doc/uid/20000162-BBCBGCDG says we must have -hash and -isEqual: workign properly for this to work, so we must do that too, using a basic xor-based hash and leveraging Cocoa -hash implementations where useful and feasible (if not necessary) +// TODO structure and rewrite this part // TODO what if this is NULL? static const CFStringRef combinedFontAttrName = CFSTR("libuiCombinedFontAttribute"); @@ -112,7 +113,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha BOOL hasHash; NSUInteger hash; } -- (void)setAttribute:(uiAttribute *)attr; +- (void)addAttribute:(uiAttribute *)attr; - (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; @end @@ -154,7 +155,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha return ret; } -- (void)setAttribute:(uiAttribute *)attr +- (void)addAttribute:(uiAttribute *)attr { int index; @@ -242,20 +243,30 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha @end -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiprivCombinedFontAttr *cfa)) +static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_t end, uiAttribute *attr) { - size_t i; - NSNumber *n; uiprivCombinedFontAttr *cfa; + CFRange range; + size_t diff; - for (i = start; i < end; i++) { - n = [NSNumber numberWithInteger:i]; - cfa = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; - if (cfa == nil) { - cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; - [p->combinedFontAttrs setObject:cfa forKey:n]; + while (start <= end) { + cfa = (uiprivCombinedFontAttr *) CFAttributedStringGetAttribute(p->mas, start, combinedFontAttrName, &range); + if (cfa == nil) + cfa = [uiprivCombinedFontAttr new]; + else + cfa = [cfa copy]; + [cfa addAttribute:attr]; + // clamp effectiveRange within [start, end) + if (range.location < start) { + diff = start - range.location; + range.location = start; + range.length -= diff; } - adj(cfa); + if ((range.location + range.length) > end) + range.length = end - range.location; + CFAttributedStringSetAttribute(p->mas, range, combinedFontAttrName, cfa); + [cfa release]; + start += effectiveRange.length; } } @@ -310,32 +321,15 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute range.location = start; range.length = end - start; switch (uiAttributeGetType(attr)) { + case uiAttributeTypeFamily: + case uiAttributeTypeSize: + case uiAttributeTypeWeight: + case uiAttributeTypeItalic: + case uiAttributeTypeStretch: + case uiAttributeTypeFeatures: + addFontAttributeToRange(p, start, end, attr); + break; $$TODO_CONTINUE_HERE - case uiAttributeFamily: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.family = spec->Family; - }); - break; - case uiAttributeSize: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.size = spec->Double; - }); - break; - case uiAttributeWeight: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.weight = (uiDrawTextWeight) (spec->Value); - }); - break; - case uiAttributeItalic: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.italic = (uiDrawTextItalic) (spec->Value); - }); - break; - case uiAttributeStretch: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.stretch = (uiDrawTextStretch) (spec->Value); - }); - break; case uiAttributeColor: color = mkcolor(spec); CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); @@ -347,6 +341,7 @@ $$TODO_CONTINUE_HERE [p->backgroundBlocks addObject:block]; Block_release(block); break; + // TODO turn into a class, like we did with the font attributes, or even integrate *into* the font attributes case uiAttributeUnderline: switch (spec->Value) { case uiDrawUnderlineStyleNone: @@ -386,84 +381,47 @@ $$TODO_CONTINUE_HERE if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; - case uiAttributeFeatures: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.features = spec->Features; - }); - break; - default: - // TODO complain - ; } return uiForEachContinue; } -static BOOL cfaIsEqual(uiprivCombinedFontAttr *a, uiprivCombinedFontAttr *b) +static void applyFontAttributes(CFMutableAttributedStringRef mas, uiDrawFontDescriptor *defaultFont) { - if (a == nil && b == nil) - return YES; - if (a == nil || b == nil) - return NO; - return [a same:b]; -} - -static void applyAndFreeFontAttributes(struct foreachParams *p) -{ - CFIndex i, n; - uiprivCombinedFontAttr *cfa, *cfab; - CTFontRef defaultFont; + uiprivCombinedFontAttr *cfa; CTFontRef font; CFRange range; + CFIndex n; - // first get the default font as a CTFontRef - cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; - defaultFont = [cfa toCTFont]; + n = CFAttributedStringGetLength(mas); + + // first apply the default font to the entire string + // TODO is this necessary given the #if 0'd code in uiprivAttributedStringToCFAttributedString()? + cfa = [uiprivCombinedFontAttr new]; + font = [cfa toCTFontWithDefaultFont:defaultFont]; [cfa release]; - - // now go through, fililng in the font attribute for successive ranges of identical combinedFontAttrs - // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself - // this also avoids breaking apart surrogate pairs (though IIRC OS X doing the something similar itself might make this a non-issue here) - cfa = nil; - n = CFAttributedStringGetLength(p->mas); range.location = 0; - for (i = 0; i < n; i++) { - NSNumber *nn; - - // TODO use NSValue or some other method of NSNumber - nn = [NSNumber numberWithInteger:i]; - cfab = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; - if (cfaIsEqual(cfa, cfab)) - continue; - - // the font has changed; write out the old one - range.length = i - range.location; - if (cfa == nil) { - // TODO this isn't necessary because of default font shenanigans below - font = defaultFont; - CFRetain(font); - } else - font = [cfa toCTFont]; - CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); - CFRelease(font); - // and start this run - cfa = cfab; - range.location = i; - } - - // and finally, write out the last range - range.length = i - range.location; - if (cfa == nil) { - // TODO likewise - font = defaultFont; - CFRetain(font); - } else - // note that this handles the difference between NULL and empty uiOpenTypeFeatures properly as far as conversion to native data formats is concerned (NULL does not generate a features dictionary; empty just produces an empty one) - // TODO but what about from the OS's perspective on all OSs? - font = [cfa toCTFont]; - CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); + range.length = n; + CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); CFRelease(font); - CFRelease(defaultFont); + // now go through, replacing every consecutive uiprivCombinedFontAttr with the proper CTFontRef + // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself + range.location = 0; + while (range.location < n) { + // TODO consider seeing if CFAttributedStringGetAttributeAndLongestEffectiveRange() can make things faster by reducing the number of potential iterations, either here or above + cfa = (uiprivCombinedFontAttr *) CFAttributedStringSetAttribute(mas, range.location, combinedFontAttrName, &range); + if (cfa != nil) { + font = [cfa toCTFontWithDefaultFont:defaultFont]; + CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); + CFRelease(font); + } + range.location += range.length; + } + + // and finally, get rid of all the uiprivCombinedFontAttrs as we won't need them anymore + range.location = 0; + range.length = 0; + CFAttributedStringRemoveAttribute(mas, range, combinedFontAttrName); } static const CTTextAlignment ctaligns[] = { @@ -531,12 +489,9 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou CFAttributedStringBeginEditing(mas); fep.mas = mas; - fep.combinedFontAttrs = [NSMutableDictionary new]; - fep.defaultFont = p->DefaultFont; fep.backgroundBlocks = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - applyAndFreeFontAttributes(&fep); - [fep.combinedFontAttrs release]; + applyFontAttributes(mas, p->DefaultFont); CFAttributedStringEndEditing(mas); *backgroundBlocks = fep.backgroundBlocks; From bf58601ff8ab6468ef8a0dfe62bcc1505a83ab41 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Mar 2018 23:16:43 -0500 Subject: [PATCH 388/487] More TODOs. --- darwin/attrstr.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index e2a53e0d..6d443648 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -72,6 +72,7 @@ struct foreachParams { // instead of incrementally adjusting CTFontRefs (which, judging from NSFontManager, seems finicky and UI-centric), we use a custom class to incrementally store attributes that go into a CTFontRef, and then convert everything to CTFonts en masse later // https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/ChangingAttrStrings.html#//apple_ref/doc/uid/20000162-BBCBGCDG says we must have -hash and -isEqual: workign properly for this to work, so we must do that too, using a basic xor-based hash and leveraging Cocoa -hash implementations where useful and feasible (if not necessary) // TODO structure and rewrite this part +// TODO re-find sources proving support of custom attributes // TODO what if this is NULL? static const CFStringRef combinedFontAttrName = CFSTR("libuiCombinedFontAttribute"); From 7451d455e5fa830f8cdd6d211218593f8588536b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 7 Mar 2018 23:53:36 -0500 Subject: [PATCH 389/487] Started a new drawtext.m with a different way to handle the empty-string crash problem. --- darwin/attrstr.m | 1 + darwin/drawtext.m | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 darwin/drawtext.m diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 6d443648..439da2c9 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -449,6 +449,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } +// TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) { CFStringRef cfstr; diff --git a/darwin/drawtext.m b/darwin/drawtext.m new file mode 100644 index 00000000..e3cc328f --- /dev/null +++ b/darwin/drawtext.m @@ -0,0 +1,127 @@ +// 7 march 2018 +#import "uipriv_darwin.h" +#import "draw.h" +#import "attrstr.h" + +// problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when gathering metrics or hit-testing +// solution: for those cases, maintain a separate framesetter just for computing those things +// in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within +struct frame { + CFAttributedStringRef attrstr; + NSArray *backgroundBlocks; + CTFramesetterRef framesetter; + CGSize size; + CGPathRef path; + CTFrameRef frame; +}; + +struct uiDrawTextLayout { + struct frame forDrawing; + struct frame forMetrics; +}; + +static void paramsToFrame(uiDrawTextLayoutParams *params, struct frame *frame) +{ + CFRange range; + CGFloat width; + CFRange unused; + CGRect rect; + + frame->attrstr = uiprivAttributedStringToCFAttributedString(p, &(frame->backgroundBlocks)); + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + frame->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); + if (frame->framesetter == NULL) { + // TODO + } + + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + + cgwidth = (CGFloat) (frame->width); + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + frame->size = CTFramesetterSuggestFrameSizeWithConstraints(frame->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL (TODO really?) + + rect.origin = CGPointZero; + rect.size = frame->size; + frame->path = CGPathCreateWithRect(rect, NULL); + frame->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + tl->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (frame->frame == NULL) { + // TODO + } +} + +static void freeFrame(struct frame *frame) +{ + CFRelease(frame->frame); + CFRelease(frame->path); + CFRelease(frame->framesetter); + [frame->backgroundBlocks release]; + CFRelease(frame->attrstr); +} + +static void retainFrameCopy(struct frame *out, const struct frame *frame) +{ + memcpy(out, frame, sizeof (struct frame)); + CFRetain(out->attrstr); + [out->backgroundBlocks retain]; + CFRetain(out->framesetter); + CFRetain(out->path); + CFRetain(out->frame); +} + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) +{ + uiDrawTextLayout *tl; + + tl = uiprivNew(uiDrawTextLayout); + paramsToFrame(p, &(tl->forDrawing)); + if (uiAttributedStringLength(p->String) != 0) + retainFrameCopy(&(tl->forMetrics), &(tl->forDrawing)); + else { + uiAttributedString *space; + uiDrawTextLayoutParams p2; + + space = uiNewAttributedString(" "); + p2 = *p; + p2.String = space; + paramsToFrame(&p2, &(tl->forMetrics)); + uiFreeAttributedString(space); + } + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + freeFrame(&(tl->forMetrics)); + freeFrame(&(tl->forDrawing)); + uiprivFree(tl); +} + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); From 5535c43bd8297ccbbdff4f2ef23067e24989c4bc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 22:38:53 -0500 Subject: [PATCH 390/487] And finished up drawtext.m for now. --- darwin/drawtext.m | 225 +++++++++++++++++++++++++++++++--------------- 1 file changed, 154 insertions(+), 71 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index e3cc328f..ed200f76 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -3,125 +3,208 @@ #import "draw.h" #import "attrstr.h" -// problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when gathering metrics or hit-testing +// problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when doing anything requiring a CTLine // solution: for those cases, maintain a separate framesetter just for computing those things // in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within -struct frame { +@interface uiprivTextFrame { CFAttributedStringRef attrstr; NSArray *backgroundBlocks; CTFramesetterRef framesetter; CGSize size; CGPathRef path; CTFrameRef frame; -}; +} +- (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p; +- (void)draw:(uiDrawContext *)c textLayout:(uiDrawTextLayout *)tl at:(double)x y:(double)y; +- (void)returnWidth:(double *)width height:(double *)height; +- (CFArrayRef)lines; +@end -struct uiDrawTextLayout { - struct frame forDrawing; - struct frame forMetrics; -}; +@implementation uiprivTextFrame -static void paramsToFrame(uiDrawTextLayoutParams *params, struct frame *frame) +- (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p { CFRange range; CGFloat width; CFRange unused; CGRect rect; - frame->attrstr = uiprivAttributedStringToCFAttributedString(p, &(frame->backgroundBlocks)); - // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing - frame->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); - if (frame->framesetter == NULL) { - // TODO - } - - range.location = 0; - range.length = CFAttributedStringGetLength(tl->attrstr); - - cgwidth = (CGFloat) (frame->width); - if (cgwidth < 0) - cgwidth = CGFLOAT_MAX; - frame->size = CTFramesetterSuggestFrameSizeWithConstraints(frame->framesetter, - range, - // TODO kCTFramePathWidthAttributeName? - NULL, - CGSizeMake(cgwidth, CGFLOAT_MAX), - &unused); // not documented as accepting NULL (TODO really?) - - rect.origin = CGPointZero; - rect.size = frame->size; - frame->path = CGPathCreateWithRect(rect, NULL); - frame->frame = CTFramesetterCreateFrame(tl->framesetter, - range, - tl->path, - // TODO kCTFramePathWidthAttributeName? - NULL); - if (frame->frame == NULL) { - // TODO + self = [super init]; + if (self) { + self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundBlocks)); + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + self->framesetter = CTFramesetterCreateWithAttributedString(self->attrstr); + if (self->framesetter == NULL) { + // TODO + } + + range.location = 0; + range.length = CFAttributedStringGetLength(self->attrstr); + + cgwidth = (CGFloat) (p->Width); + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + self->size = CTFramesetterSuggestFrameSizeWithConstraints(self->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL (TODO really?) + + rect.origin = CGPointZero; + rect.size = self->size; + self->path = CGPathCreateWithRect(rect, NULL); + self->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + self->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (self->frame == NULL) { + // TODO + } } + return self; } -static void freeFrame(struct frame *frame) +- (void)dealloc { - CFRelease(frame->frame); - CFRelease(frame->path); - CFRelease(frame->framesetter); - [frame->backgroundBlocks release]; - CFRelease(frame->attrstr); + CFRelease(self->frame); + CFRelease(self->path); + CFRelease(self->framesetter); + [self->backgroundBlocks release]; + CFRelease(self->attrstr); + [super dealloc]; } -static void retainFrameCopy(struct frame *out, const struct frame *frame) +- (void)draw:(uiDrawContext *)c textLayout:(uiDrawTextLayout *)tl at:(double)x y:(double)y { - memcpy(out, frame, sizeof (struct frame)); - CFRetain(out->attrstr); - [out->backgroundBlocks retain]; - CFRetain(out->framesetter); - CFRetain(out->path); - CFRetain(out->frame); + backgroundBlock b; + CGAffineTransform textMatrix; + + CGContextSaveGState(c->c); + // save the text matrix because it's not part of the graphics state + textMatrix = CGContextGetTextMatrix(c->c); + + for (b in self->backgroundBlocks) + b(c, tl, x, y); + + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped + // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) + // TODO how is this affected by a non-identity CTM? + CGContextTranslateCTM(c->c, 0, c->height); + CGContextScaleCTM(c->c, 1.0, -1.0); + CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); + + // wait, that's not enough; we need to offset y values to account for our new flipping + // TODO explain this calculation + y = c->height - self->size.height - y; + + // CTFrameDraw() draws in the path we specified when creating the frame + // this means that in our usage, CTFrameDraw() will draw at (0,0) + // so move the origin to be at (x,y) instead + // TODO are the signs correct? + CGContextTranslateCTM(c->c, x, y); + + CTFrameDraw(self->frame, c->c); + + CGContextSetTextMatrix(c->c, textMatrix); + CGContextRestoreGState(c->c); } +- (void)returnWidth:(double *)width height:(double *)height +{ + if (width != NULL) + *width = self->size.width; + if (height != NULL) + *height = self->size.height; +} + +- (CFArrayRef)lines +{ + return CTFrameGetLines(self->frame); +} + +@end + +struct uiDrawTextLayout { + uiprivTextFrame *frame; + uiprivTextFrame *forLines; + BOOL empty; + + // for converting CFAttributedString indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; + size_t *u16tou8; + size_t nUTF16; +}; + uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; tl = uiprivNew(uiDrawTextLayout); - paramsToFrame(p, &(tl->forDrawing)); + tl->frame = [[uiprivTextFrame alloc] initWithLayoutParams:p]; if (uiAttributedStringLength(p->String) != 0) - retainFrameCopy(&(tl->forMetrics), &(tl->forDrawing)); + tl->forLines = [tl->frame retain]; else { uiAttributedString *space; uiDrawTextLayoutParams p2; + tl->empty = YES; space = uiNewAttributedString(" "); p2 = *p; p2.String = space; - paramsToFrame(&p2, &(tl->forMetrics)); + tl->forLines = [[uiprivTextFrame alloc] initWithLayoutParams:&p2]; uiFreeAttributedString(space); } + + // and finally copy the UTF-8/UTF-16 conversion tables + tl->u8tou16 = uiprivAttributedStringCopyUTF8ToUTF16Table(p->String, &(tl->nUTF8)); + tl->u16tou8 = uiprivAttributedStringCopyUTF16ToUTF8Table(p->String, &(tl->nUTF16)); return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - freeFrame(&(tl->forMetrics)); - freeFrame(&(tl->forDrawing)); + uiprivFree(tl->u16tou8); + uiprivFree(tl->u8tou16); + [tl->forLines release]; + [tl->frame release]; uiprivFree(tl); } -// uiDrawText() draws tl in c with the top-left point of tl at (x, y). -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); +// TODO document that (x,y) is the top-left corner of the *entire frame* +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + [tl->frame draw:c textLayout:tl at:x y:y]; +} -// uiDrawTextLayoutExtents() returns the width and height of tl -// in width and height. The returned width may be smaller than -// the width passed into uiDrawNewTextLayout() depending on -// how the text in tl is wrapped. Therefore, you can use this -// function to get the actual size of the text layout. -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines +// TODO width doesn't include trailing whitespace... +// TODO figure out how paragraph spacing should play into this +// TODO standardize and document the behavior of this on an empty layout +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + // TODO explain this, given the above + [tl->frame returnWidth:width height:NULL]; + [tl->forLines returnWidth:NULL height:height]; +} -// uiDrawTextLayoutNumLines() returns the number of lines in tl. -// This number will always be greater than or equal to 1; a text -// layout with no text only has one line. -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return CFArrayGetCount([tl->forLines lines]); +} -// uiDrawTextLayoutLineByteRange() returns the byte indices of the -// text that falls into the given line of tl as [start, end). -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + CTLineRef lr; + CFRange range; + + lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line); + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + if (tl->empty) + *end = *start; + else + *end = tl->u16tou8[range.location + range.length]; +} From 9661d142625a0340160ad139e39cb6f73a8c2bab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 22:44:35 -0500 Subject: [PATCH 391/487] And fixed up loose ends preventing a build. Let's build! --- common/CMakeLists.txt | 4 +++- common/attrstr.h | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 8ecff8b3..2d7d08d9 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,13 +1,15 @@ # 3 june 2016 list(APPEND _LIBUI_SOURCES + common/attribute.c common/attrlist.c common/attrstr.c common/areaevents.c common/control.c common/debug.c - common/drawtext.c +# common/drawtext.c common/matrix.c + common/opentype.c common/shouldquit.c common/userbugs.c common/utf.c diff --git a/common/attrstr.h b/common/attrstr.h index 32e632ed..fb96c761 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,5 +1,11 @@ // 19 february 2018 +// TODO remove when done migrating these functions +#define uiprivNew(x) uiNew(x) +#define uiprivAlloc(x, y) uiAlloc(x, y) +#define uiprivRealloc(x, y) uiRealloc(x, y) +#define uiprivFree(x) uiFree(x) + // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); extern void uiprivAttributeRelease(uiAttribute *a); From e9a62461c2770c24ad7a174fab015d8a6ff7c8ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 23:04:45 -0500 Subject: [PATCH 392/487] Started fixing compile errors. I forgot I renamed uiDrawFontDescriptor to uiFontDescriptor! --- common/attribute.c | 2 +- darwin/attrstr.h | 8 ++++---- darwin/attrstr.m | 14 +++++++------- darwin/fontbutton.m | 8 ++++---- darwin/fontmatch.m | 14 +++++++------- darwin/fonttraits.m | 4 ++-- darwin/fontvariation.m | 2 +- ui_attrstr.h | 6 +++--- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/common/attribute.c b/common/attribute.c index 79273bef..a6c0b4c0 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -206,7 +206,7 @@ uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g return at; } -void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha) +void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha) { *u = a->u.color.underlineColor; uiAttributeColor(a, r, g, b, alpha); diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 14a1cbe5..384a607b 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -60,16 +60,16 @@ extern void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, - (CFStringRef)familyName; - (CFArrayRef)variationAxes; @end -extern CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef uiprivFontDescriptorToCTFontDescriptor(uiFontDescriptor *fd); extern CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); -extern void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiFontDescriptor *uidesc); // fonttraits.m -extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out); +extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiFontDescriptor *out); // fontvariation.m extern NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); -extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); +extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiFontDescriptor *out); // attrstr.m extern void uiprivInitUnderlineColors(void); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 439da2c9..ad2dfeda 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -58,7 +58,7 @@ void uiprivUninitUnderlineColors(void) spellingColor = nil; } -// TODO opentype features are lost when using uiDrawFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? +// TODO opentype features are lost when using uiFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? @@ -115,7 +115,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha NSUInteger hash; } - (void)addAttribute:(uiAttribute *)attr; -- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; +- (CTFontRef)toCTFontWithDefaultFont:(uiFontDescriptor *)defaultFont; @end @implementation uiprivCombinedFontAttr @@ -216,15 +216,15 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha return self->hash; } -- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont +- (CTFontRef)toCTFontWithDefaultFont:(uiFontDescriptor *)defaultFont { - uiDrawFontDescriptor uidesc; + uiFontDescriptor uidesc; CTFontDescriptorRef desc; CTFontRef font; uidesc = *defaultFont; if (self->attrs[cFamily] != NULL) - // TODO const-correct uiDrawFontDescriptor or change this function below + // TODO const-correct uiFontDescriptor or change this function below uidesc.Family = (char *) uiAttributeFamily(self->attrs[cFamily]); if (self->attrs[cSize] != NULL) uidesc.Size = uiAttributeSize(self->attrs[cSize]); @@ -234,7 +234,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha uidesc.Italic = uiAttributeItalic(self->attrs[cItalic]); if (self->attrs[cStretch] != NULL) uidesc.Stretch = uiAttributeStretch(self->attrs[cStretch]); - desc = uiprivDrawFontDescriptorToCTFontDescriptor(&uidesc); + desc = uiprivFontDescriptorToCTFontDescriptor(&uidesc); if (self->attrs[cFeatures] != NULL) desc = uiprivCTFontDescriptorAppendFeatures(desc, uiAttributeFeatures(self->attrs[cFeatures])); font = CTFontCreateWithFontDescriptor(desc, uidesc.Size, NULL); @@ -386,7 +386,7 @@ $$TODO_CONTINUE_HERE return uiForEachContinue; } -static void applyFontAttributes(CFMutableAttributedStringRef mas, uiDrawFontDescriptor *defaultFont) +static void applyFontAttributes(CFMutableAttributedStringRef mas, uiFontDescriptor *defaultFont) { uiprivCombinedFontAttr *cfa; CTFontRef font; diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index 38205135..86f1c140 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -12,7 +12,7 @@ - (void)activateFontButton; - (void)deactivateFontButton:(BOOL)activatingAnother; - (void)deactivateOnClose:(NSNotification *)note; -- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc; +- (void)getfontdesc:(uiFontDescriptor *)uidesc; @end // only one may be active at one time @@ -139,14 +139,14 @@ struct uiFontButton { NSFontPanelCollectionModeMask; } -- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc +- (void)getfontdesc:(uiFontDescriptor *)uidesc { CTFontRef ctfont; CTFontDescriptorRef ctdesc; ctfont = (CTFontRef) (self->libui_font); ctdesc = CTFontCopyFontDescriptor(ctfont); - uiprivDrawFontDescriptorFromCTFontDescriptor(ctdesc, uidesc); + uiprivFontDescriptorFromCTFontDescriptor(ctdesc, uidesc); CFRelease(ctdesc); uidesc->Size = CTFontGetSize(ctfont); } @@ -200,7 +200,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) +void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc) { [b->button getfontdesc:desc]; } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 6a9eb14e..2c3de4bc 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -18,7 +18,7 @@ // // To make things easier for us, we'll match by converting Core // Text's values back into libui values. This allows us to also use the -// normalization code for filling in uiDrawFontDescriptors from +// normalization code for filling in uiFontDescriptors from // Core Text fonts and font descriptors. // // Style matching needs to be done early in the font loading process; @@ -318,14 +318,14 @@ static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static void setItalic(uiprivFontStyleData *d, uiDrawFontDescriptor *out) +static void setItalic(uiprivFontStyleData *d, uiFontDescriptor *out) { out->Italic = uiDrawTextItalicNormal; if (([d symbolicTraits] & kCTFontItalicTrait) != 0) out->Italic = guessItalicOblique(d); } -static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiFontDescriptor *out) { setItalic(d, out); if (axisDict != nil) @@ -334,7 +334,7 @@ static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiprivProcessFontTraits(d, out); } -static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDescriptor *styles) +static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiFontDescriptor *styles) { CFArrayRef matching; CFIndex i, n; @@ -363,7 +363,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { - uiDrawFontDescriptor fields; + uiFontDescriptor fields; closeness[i].index = i; if (i != 0) { @@ -413,7 +413,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes return out; } -CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd) +CTFontDescriptorRef uiprivFontDescriptorToCTFontDescriptor(uiFontDescriptor *fd) { CFMutableDictionaryRef attrs; CFStringRef cffamily; @@ -465,7 +465,7 @@ CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef des return new; } -void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) +void uiprivFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiFontDescriptor *uidesc) { CFStringRef cffamily; uiprivFontStyleData *d; diff --git a/darwin/fonttraits.m b/darwin/fonttraits.m index b4b932c8..b3989c37 100644 --- a/darwin/fonttraits.m +++ b/darwin/fonttraits.m @@ -54,7 +54,7 @@ static const CFStringRef exceptions[] = { NULL, }; -static void trySecondaryOS2Values(uiprivFontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +static void trySecondaryOS2Values(uiprivFontStyleData *d, uiFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) { CFDataRef os2; uint16_t usWeightClass, usWidthClass; @@ -159,7 +159,7 @@ static BOOL shouldReallyBeSemiCondensed(uiprivFontStyleData *d) return testTTFOTFSubfamilyNames(d, CFSTR("Semi Condensed")); } -void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out) +void uiprivProcessFontTraits(uiprivFontStyleData *d, uiFontDescriptor *out) { double weight, width; BOOL hasWeight, hasWidth; diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 9959b02b..d59f0c7a 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -305,7 +305,7 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, return YES; } -void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiFontDescriptor *out) { CFDictionaryRef var; double v; diff --git a/ui_attrstr.h b/ui_attrstr.h index 9b02035d..3a22e6ed 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -214,7 +214,7 @@ _UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double // uiAttributeUnderlineColor() returns the underline color stored in // a. It is an error to call this on a uiAttribute that does not hold an // underline color. -_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); +_UI_EXTERN void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); // uiOpenTypeFeatures represents a set of OpenType feature // tag-value pairs, for applying OpenType features to text. @@ -409,7 +409,7 @@ _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, s // All the members operate like the respective uiAttributes. typedef struct uiFontDescriptor uiFontDescriptor; -struct uiDrawFontDescriptor { +struct uiFontDescriptor { // TODO const-correct this or figure out how to deal with this when getting a value char *Family; double Size; @@ -448,7 +448,7 @@ typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; // TODO const-correct this somehow struct uiDrawTextLayoutParams { uiAttributedString *String; - uiDrawFontDescriptor *DefaultFont; + uiFontDescriptor *DefaultFont; double Width; uiDrawTextAlign Align; }; From 82d3de7c31ec37fa5a0c9a525612b4a6bb7b0a84 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 23:27:04 -0500 Subject: [PATCH 393/487] More compiler error fixes. Oops, I forgot to finish attrstr.m! --- common/attribute.c | 4 ++-- common/attrstr.c | 4 ++-- common/attrstr.h | 4 ++-- darwin/attrstr.m | 59 ++++++++++++++++++++++++---------------------- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/common/attribute.c b/common/attribute.c index a6c0b4c0..0e30cb2e 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -4,7 +4,7 @@ #include "attrstr.h" struct uiAttribute { - int owned; + int ownedByUser; size_t refcount; uiAttributeType type; union { @@ -174,7 +174,7 @@ uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a) { uiAttribute *at; - at = newAttribute(uiAttributeTypeBackgroundColor); + at = newAttribute(uiAttributeTypeBackground); at->u.color.r = r; at->u.color.g = g; at->u.color.b = b; diff --git a/common/attrstr.c b/common/attrstr.c index ab3f073a..1a0f6d9f 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -279,9 +279,9 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) resize(s, start + count, start16 + count16); } -void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end) { - uiprivAttrListInsertAttribute(s->attrs, spec, start, end); + uiprivAttrListInsertAttribute(s->attrs, a, start, end); } // LONGTERM introduce an iterator object instead? diff --git a/common/attrstr.h b/common/attrstr.h index fb96c761..1dae7d63 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -3,7 +3,7 @@ // TODO remove when done migrating these functions #define uiprivNew(x) uiNew(x) #define uiprivAlloc(x, y) uiAlloc(x, y) -#define uiprivRealloc(x, y) uiRealloc(x, y) +#define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) // attribute.c @@ -21,7 +21,7 @@ extern void uiprivFreeAttrList(uiprivAttrList *alist); extern void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end); extern void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count); extern void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count); -extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute type, size_t start, size_t end); +extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttributeType type, size_t start, size_t end); extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index ad2dfeda..f067956a 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -53,7 +53,7 @@ void uiprivUninitUnderlineColors(void) [auxiliaryColor release]; auxiliaryColor = nil; [grammarColor release]; - grammarColors = nil; + grammarColor = nil; [spellingColor release]; spellingColor = nil; } @@ -267,7 +267,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ range.length = end - range.location; CFAttributedStringSetAttribute(p->mas, range, combinedFontAttrName, cfa); [cfa release]; - start += effectiveRange.length; + start += range.length; } } @@ -285,7 +285,7 @@ static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, dou }); } -static CGColorRef mkcolor(uiAttributeSpec *spec) +static CGColorRef mkcolor(double r, double g, double b, double a) { CGColorSpaceRef colorspace; CGColorRef color; @@ -296,10 +296,10 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) if (colorspace == NULL) { // TODO } - components[0] = spec->R; - components[1] = spec->G; - components[2] = spec->B; - components[3] = spec->A; + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = a; color = CGColorCreate(colorspace, components); CFRelease(colorspace); return color; @@ -314,6 +314,8 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute backgroundBlock block; int32_t us; CFNumberRef num; + double r, g, b, a; + uiUnderlineColor colorType; ostart = start; oend = end; @@ -330,31 +332,31 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute case uiAttributeTypeFeatures: addFontAttributeToRange(p, start, end, attr); break; -$$TODO_CONTINUE_HERE - case uiAttributeColor: - color = mkcolor(spec); + case uiAttributeTypeColor: + uiAttributeColor(attr, &r, &g, &b, &a); + color = mkcolor(r, g, b, a); CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); CFRelease(color); break; - case uiAttributeBackground: - block = mkBackgroundBlock(ostart, oend, - spec->R, spec->G, spec->B, spec->A); + case uiAttributeTypeBackground: + uiAttributeColor(attr, &r, &g, &b, &a); + block = mkBackgroundBlock(ostart, oend, r, g, b, a); [p->backgroundBlocks addObject:block]; Block_release(block); break; // TODO turn into a class, like we did with the font attributes, or even integrate *into* the font attributes - case uiAttributeUnderline: - switch (spec->Value) { - case uiDrawUnderlineStyleNone: + case uiAttributeTypeUnderline: + switch (uiAttributeUnderline(attr)) { + case uiUnderlineNone: us = kCTUnderlineStyleNone; break; - case uiDrawUnderlineStyleSingle: + case uiUnderlineSingle: us = kCTUnderlineStyleSingle; break; - case uiDrawUnderlineStyleDouble: + case uiUnderlineDouble: us = kCTUnderlineStyleDouble; break; - case uiDrawUnderlineStyleSuggestion: + case uiUnderlineSuggestion: // TODO incorrect if a solid color us = kCTUnderlineStyleThick; break; @@ -363,23 +365,24 @@ $$TODO_CONTINUE_HERE CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineStyleAttributeName, num); CFRelease(num); break; - case uiAttributeUnderlineColor: - switch (spec->Value) { - case uiDrawUnderlineColorCustom: - color = mkcolor(spec); + case uiAttributeTypeUnderlineColor: + uiAttributeUnderlineColor(attr, &colorType, &r, &g, &b, &a); + switch (colorType) { + case uiUnderlineColorCustom: + color = mkcolor(r, g, b, a); break; - case uiDrawUnderlineColorSpelling: + case uiUnderlineColorSpelling: color = [spellingColor CGColor]; break; - case uiDrawUnderlineColorGrammar: + case uiUnderlineColorGrammar: color = [grammarColor CGColor]; break; - case uiDrawUnderlineColorAuxiliary: + case uiUnderlineColorAuxiliary: color = [auxiliaryColor CGColor]; break; } CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineColorAttributeName, color); - if (spec->Value == uiDrawUnderlineColorCustom) + if (colorType == uiUnderlineColorCustom) CFRelease(color); break; } @@ -410,7 +413,7 @@ static void applyFontAttributes(CFMutableAttributedStringRef mas, uiFontDescript range.location = 0; while (range.location < n) { // TODO consider seeing if CFAttributedStringGetAttributeAndLongestEffectiveRange() can make things faster by reducing the number of potential iterations, either here or above - cfa = (uiprivCombinedFontAttr *) CFAttributedStringSetAttribute(mas, range.location, combinedFontAttrName, &range); + cfa = (uiprivCombinedFontAttr *) CFAttributedStringGetAttribute(mas, range.location, combinedFontAttrName, &range); if (cfa != nil) { font = [cfa toCTFontWithDefaultFont:defaultFont]; CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); From c82197f4089a9e1c9f46ddd411d8029d7de2c309 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 9 Mar 2018 18:01:23 -0500 Subject: [PATCH 394/487] And fixed other compile errors. Of course I forgot to add uiFontButton back in (I wanted to properly comment that first :/ ). Now for linker errors, which may result in some warning fixes along the way. --- darwin/drawtext.m | 8 ++--- darwin/fontmatch.m | 18 +++++------ darwin/fonttraits.m | 68 +++++++++++++++++++++--------------------- darwin/fontvariation.m | 10 +++---- darwin/opentype.m | 2 +- old_ui_attrstr.h | 8 ----- ui_attrstr.h | 8 +++++ 7 files changed, 61 insertions(+), 61 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index ed200f76..21c6e45e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -6,7 +6,7 @@ // problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when doing anything requiring a CTLine // solution: for those cases, maintain a separate framesetter just for computing those things // in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within -@interface uiprivTextFrame { +@interface uiprivTextFrame : NSObject { CFAttributedStringRef attrstr; NSArray *backgroundBlocks; CTFramesetterRef framesetter; @@ -25,7 +25,7 @@ - (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p { CFRange range; - CGFloat width; + CGFloat cgwidth; CFRange unused; CGRect rect; @@ -54,7 +54,7 @@ rect.origin = CGPointZero; rect.size = self->size; self->path = CGPathCreateWithRect(rect, NULL); - self->frame = CTFramesetterCreateFrame(tl->framesetter, + self->frame = CTFramesetterCreateFrame(self->framesetter, range, self->path, // TODO kCTFramePathWidthAttributeName? @@ -144,7 +144,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) tl = uiprivNew(uiDrawTextLayout); tl->frame = [[uiprivTextFrame alloc] initWithLayoutParams:p]; - if (uiAttributedStringLength(p->String) != 0) + if (uiAttributedStringLen(p->String) != 0) tl->forLines = [tl->frame retain]; else { uiAttributedString *space; diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 2c3de4bc..9645662f 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -275,9 +275,9 @@ FONTNAME(familyName, struct closeness { CFIndex index; - uiDrawTextWeight weight; + uiTextWeight weight; double italic; - uiDrawTextStretch stretch; + uiTextStretch stretch; double distance; }; @@ -287,14 +287,14 @@ static const double italicClosenessNormal[] = { 0, 1, 1 }; static const double italicClosenessOblique[] = { 1, 0, 0.5 }; static const double italicClosenessItalic[] = { 1, 0.5, 0 }; static const double *italicClosenesses[] = { - [uiDrawTextItalicNormal] = italicClosenessNormal, - [uiDrawTextItalicOblique] = italicClosenessOblique, - [uiDrawTextItalicItalic] = italicClosenessItalic, + [uiTextItalicNormal] = italicClosenessNormal, + [uiTextItalicOblique] = italicClosenessOblique, + [uiTextItalicItalic] = italicClosenessItalic, }; // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) +static uiTextItalic guessItalicOblique(uiprivFontStyleData *d) { CFStringRef styleName; BOOL isOblique; @@ -309,8 +309,8 @@ static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) isOblique = YES; } if (isOblique) - return uiDrawTextItalicOblique; - return uiDrawTextItalicItalic; + return uiTextItalicOblique; + return uiTextItalicItalic; } // Italics are hard because Core Text does NOT distinguish between italic and oblique. @@ -320,7 +320,7 @@ static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) static void setItalic(uiprivFontStyleData *d, uiFontDescriptor *out) { - out->Italic = uiDrawTextItalicNormal; + out->Italic = uiTextItalicNormal; if (([d symbolicTraits] & kCTFontItalicTrait) != 0) out->Italic = guessItalicOblique(d); } diff --git a/darwin/fonttraits.m b/darwin/fonttraits.m index b3989c37..a51492c9 100644 --- a/darwin/fonttraits.m +++ b/darwin/fonttraits.m @@ -31,18 +31,18 @@ static BOOL fontRegistered(uiprivFontStyleData *d) // Core Text does (usWidthClass / 10) - 0.5 here. // This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. // We'll just treat them as identical to 1 and 9, respectively. -static const uiDrawTextStretch os2WidthsToStretches[] = { - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchExtraCondensed, - uiDrawTextStretchCondensed, - uiDrawTextStretchSemiCondensed, - uiDrawTextStretchNormal, - uiDrawTextStretchSemiExpanded, - uiDrawTextStretchExpanded, - uiDrawTextStretchExtraExpanded, - uiDrawTextStretchUltraExpanded, - uiDrawTextStretchUltraExpanded, +static const uiTextStretch os2WidthsToStretches[] = { + uiTextStretchUltraCondensed, + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, + uiTextStretchUltraExpanded, }; static const CFStringRef exceptions[] = { @@ -174,50 +174,50 @@ void uiprivProcessFontTraits(uiprivFontStyleData *d, uiFontDescriptor *out) if (!hasWeight) // TODO this scale is a bit lopsided if (weight <= -0.7) - out->Weight = uiDrawTextWeightThin; + out->Weight = uiTextWeightThin; else if (weight <= -0.5) - out->Weight = uiDrawTextWeightUltraLight; + out->Weight = uiTextWeightUltraLight; else if (weight <= -0.3) - out->Weight = uiDrawTextWeightLight; + out->Weight = uiTextWeightLight; else if (weight <= -0.23) { - out->Weight = uiDrawTextWeightBook; + out->Weight = uiTextWeightBook; if (shouldReallyBeThin(d)) - out->Weight = uiDrawTextWeightThin; + out->Weight = uiTextWeightThin; } else if (weight <= 0.0) - out->Weight = uiDrawTextWeightNormal; + out->Weight = uiTextWeightNormal; else if (weight <= 0.23) - out->Weight = uiDrawTextWeightMedium; + out->Weight = uiTextWeightMedium; else if (weight <= 0.3) - out->Weight = uiDrawTextWeightSemiBold; + out->Weight = uiTextWeightSemiBold; else if (weight <= 0.4) - out->Weight = uiDrawTextWeightBold; + out->Weight = uiTextWeightBold; else if (weight <= 0.5) - out->Weight = uiDrawTextWeightUltraBold; + out->Weight = uiTextWeightUltraBold; else if (weight <= 0.7) - out->Weight = uiDrawTextWeightHeavy; + out->Weight = uiTextWeightHeavy; else - out->Weight = uiDrawTextWeightUltraHeavy; + out->Weight = uiTextWeightUltraHeavy; if (!hasWidth) // TODO this scale is a bit lopsided if (width <= -0.7) { - out->Stretch = uiDrawTextStretchUltraCondensed; + out->Stretch = uiTextStretchUltraCondensed; if (shouldReallyBeSemiCondensed(d)) - out->Stretch = uiDrawTextStretchSemiCondensed; + out->Stretch = uiTextStretchSemiCondensed; } else if (width <= -0.5) - out->Stretch = uiDrawTextStretchExtraCondensed; + out->Stretch = uiTextStretchExtraCondensed; else if (width <= -0.2) - out->Stretch = uiDrawTextStretchCondensed; + out->Stretch = uiTextStretchCondensed; else if (width <= -0.1) - out->Stretch = uiDrawTextStretchSemiCondensed; + out->Stretch = uiTextStretchSemiCondensed; else if (width <= 0.0) - out->Stretch = uiDrawTextStretchNormal; + out->Stretch = uiTextStretchNormal; else if (width <= 0.1) - out->Stretch = uiDrawTextStretchSemiExpanded; + out->Stretch = uiTextStretchSemiExpanded; else if (width <= 0.2) - out->Stretch = uiDrawTextStretchExpanded; + out->Stretch = uiTextStretchExpanded; else if (width <= 0.6) - out->Stretch = uiDrawTextStretchExtraExpanded; + out->Stretch = uiTextStretchExtraExpanded; else - out->Stretch = uiDrawTextStretchUltraExpanded; + out->Stretch = uiTextStretchUltraExpanded; } diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index d59f0c7a..60a124bd 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -310,8 +310,8 @@ void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, CFDictionaryRef var; double v; - out->Weight = uiDrawTextWeightNormal; - out->Stretch = uiDrawTextStretchNormal; + out->Weight = uiTextWeightNormal; + out->Stretch = uiTextStretchNormal; var = [d variation]; @@ -320,14 +320,14 @@ void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, // we want a linear value between 0 and 1000 with 400 being normal if (v < 0) { v += 1; - out->Weight = (uiDrawTextWeight) (v * 400); + out->Weight = (uiTextWeight) (v * 400); } else if (v > 0) - out->Weight += (uiDrawTextWeight) (v * 600); + out->Weight += (uiTextWeight) (v * 600); } if (tryAxis(axisDict, var, fvarAxisKey(fvarWidth), &v)) { // likewise, but with stretches, we go from 0 to 8 with 4 being directly between the two, so this is sufficient v += 1; - out->Stretch = (uiDrawTextStretch) (v * 4); + out->Stretch = (uiTextStretch) (v * 4); } } diff --git a/darwin/opentype.m b/darwin/opentype.m index 079197ed..84dbde36 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -92,7 +92,7 @@ static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b p.valueValue = (const SInt32 *) (&value); addCTFeatureEntry(&p); - CFRelease(strTag); + CFRelease(tagstr); return uiForEachContinue; } diff --git a/old_ui_attrstr.h b/old_ui_attrstr.h index 5dfc921c..75df996d 100644 --- a/old_ui_attrstr.h +++ b/old_ui_attrstr.h @@ -61,11 +61,3 @@ _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking // TODO allow secondary carets - -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? -_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); -// TOOD SetFont, mechanics -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -_UI_EXTERN uiFontButton *uiNewFontButton(void); diff --git a/ui_attrstr.h b/ui_attrstr.h index 3a22e6ed..6dd738ee 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -495,3 +495,11 @@ _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, si // TODO metrics functions // TODO number of lines visible for clipping rect, range visible for clipping rect? + +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); +// TOOD SetFont, mechanics +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +_UI_EXTERN uiFontButton *uiNewFontButton(void); From 9194ba29fe2d7dfddfdb1b828e426ceb751af46c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:02:10 -0500 Subject: [PATCH 395/487] And fixed build errors. Now I'm sure there are warnings that are real bugs, so let's handle those next. --- common/attrstr.h | 2 ++ darwin/attrstr.m | 4 ++-- darwin/fontmatch.m | 2 +- darwin/main.m | 5 +++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index 1dae7d63..d0ff049b 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,6 +5,8 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) +#define uiprivStrdup(x) strdup(x) +#define uiprivStricmp(x, y) strcasecmp(x, y) // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index f067956a..cc42d4c6 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -281,7 +281,7 @@ static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, dou brush.G = g; brush.B = b; brush.A = a; - drawTextBackground(c, x, y, layout, start, end, &brush, 0); +//TODO drawTextBackground(c, x, y, layout, start, end, &brush, 0); }); } @@ -463,7 +463,7 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou CFMutableAttributedStringRef mas; struct foreachParams fep; - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); + cfstr = CFStringCreateWithCharacters(NULL, uiprivAttributedStringUTF16String(p->String), uiprivAttributedStringUTF16Len(p->String)); if (cfstr == NULL) { // TODO } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 9645662f..f459a165 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -450,7 +450,7 @@ CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef des CFDictionaryRef attrs; const void *keys[1], *values[1]; - featuresArray = otfToFeaturesArray(otf); + featuresArray = uiprivOpenTypeFeaturesToCTFeatures(otf); keys[0] = kCTFontFeatureSettingsAttribute; values[0] = featuresArray; attrs = CFDictionaryCreate(NULL, diff --git a/darwin/main.m b/darwin/main.m index 12c528ff..865fb942 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -1,5 +1,6 @@ // 6 april 2015 #import "uipriv_darwin.h" +#import "attrstr.h" static BOOL canQuit = NO; static NSAutoreleasePool *globalPool; @@ -128,7 +129,7 @@ const char *uiInit(uiInitOptions *o) uiprivSetupFontPanel(); - initUnderlineColors(); + uiprivInitUnderlineColors(); } globalPool = [[NSAutoreleasePool alloc] init]; @@ -144,7 +145,7 @@ void uiUninit(void) [globalPool release]; @autoreleasepool { - uninitUnderlineColors(); + uiprivUninitUnderlineColors(); [delegate release]; [realNSApp() setDelegate:nil]; [app release]; From 115a60c950e9223a01bd5e5a0cf2a2205fe6910b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:13:20 -0500 Subject: [PATCH 396/487] uiAttribute copies family names, so its internal copy doesn't need to be const. Fix this. --- common/attribute.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/attribute.c b/common/attribute.c index 0e30cb2e..de0bdc13 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -8,7 +8,7 @@ struct uiAttribute { size_t refcount; uiAttributeType type; union { - const char *family; + char *family; double size; uiTextWeight weight; uiTextItalic italic; From 4bb6a56c99cd2c558c26c766b9b6aae228863b90 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:21:39 -0500 Subject: [PATCH 397/487] More warning fixes. Made private uiAttributeString functions const-correct to expose more potential issues later. Const-correctness and uiAttributeRetain() are going to be an issue... --- common/attrstr.c | 10 +++++----- common/attrstr.h | 10 +++++----- common/opentype.c | 1 + darwin/attrstr.m | 1 - 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 1a0f6d9f..ee2b1616 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -316,23 +316,23 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) // helpers for platform-specific code -const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s) +const uint16_t *uiprivAttributedStringUTF16String(const uiAttributedString *s) { return s->u16; } -size_t uiprivAttributedStringUTF16Len(uiAttributedString *s) +size_t uiprivAttributedStringUTF16Len(const uiAttributedString *s) { return s->u16len; } // TODO is this still needed given the below? -size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n) +size_t uiprivAttributedStringUTF8ToUTF16(const uiAttributedString *s, size_t n) { return s->u8tou16[n]; } -size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; @@ -344,7 +344,7 @@ size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t return out; } -size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; diff --git a/common/attrstr.h b/common/attrstr.h index d0ff049b..94fe934f 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -29,11 +29,11 @@ extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, extern void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // attrstr.c -extern const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s); -extern size_t uiprivAttributedStringUTF16Len(uiAttributedString *s); -extern size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n); -extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n); -extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n); +extern const uint16_t *uiprivAttributedStringUTF16String(const uiAttributedString *s); +extern size_t uiprivAttributedStringUTF16Len(const uiAttributedString *s); +extern size_t uiprivAttributedStringUTF8ToUTF16(const uiAttributedString *s, size_t n); +extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedString *s, size_t *n); +extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n); // per-OS graphemes.c/graphemes.cpp/graphemes.m/etc. struct graphemes { diff --git a/common/opentype.c b/common/opentype.c index 625f5852..e579b612 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -1,4 +1,5 @@ // 25 february 2018 +#include #include "../ui.h" #include "uipriv.h" #include "attrstr.h" diff --git a/darwin/attrstr.m b/darwin/attrstr.m index cc42d4c6..c53daae7 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -457,7 +457,6 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; - CTFontRef defaultCTFont; CTParagraphStyleRef ps; CFAttributedStringRef base; CFMutableAttributedStringRef mas; From 8fda4071834ccecc7c07b95780a7500fd66b0c51 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:24:20 -0500 Subject: [PATCH 398/487] Moved the old drawtext example out of the way. --- .../examples_drawtext}/attributes.c | 0 .../examples_drawtext}/basic.c | 0 .../examples_drawtext}/drawtext.h | 0 .../examples_drawtext}/emptystr_hittest.c | 0 .../examples_drawtext}/hittest.c | 0 .../examples_drawtext}/httext.gz | Bin .../examples_drawtext}/main.c | 0 _wip/examples_drawtext_CMakeLists.txt | 58 ++++++++++++++++++ examples/CMakeLists.txt | 11 ---- 9 files changed, 58 insertions(+), 11 deletions(-) rename {examples/drawtext => _wip/examples_drawtext}/attributes.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/basic.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/drawtext.h (100%) rename {examples/drawtext => _wip/examples_drawtext}/emptystr_hittest.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/hittest.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/httext.gz (100%) rename {examples/drawtext => _wip/examples_drawtext}/main.c (100%) create mode 100644 _wip/examples_drawtext_CMakeLists.txt diff --git a/examples/drawtext/attributes.c b/_wip/examples_drawtext/attributes.c similarity index 100% rename from examples/drawtext/attributes.c rename to _wip/examples_drawtext/attributes.c diff --git a/examples/drawtext/basic.c b/_wip/examples_drawtext/basic.c similarity index 100% rename from examples/drawtext/basic.c rename to _wip/examples_drawtext/basic.c diff --git a/examples/drawtext/drawtext.h b/_wip/examples_drawtext/drawtext.h similarity index 100% rename from examples/drawtext/drawtext.h rename to _wip/examples_drawtext/drawtext.h diff --git a/examples/drawtext/emptystr_hittest.c b/_wip/examples_drawtext/emptystr_hittest.c similarity index 100% rename from examples/drawtext/emptystr_hittest.c rename to _wip/examples_drawtext/emptystr_hittest.c diff --git a/examples/drawtext/hittest.c b/_wip/examples_drawtext/hittest.c similarity index 100% rename from examples/drawtext/hittest.c rename to _wip/examples_drawtext/hittest.c diff --git a/examples/drawtext/httext.gz b/_wip/examples_drawtext/httext.gz similarity index 100% rename from examples/drawtext/httext.gz rename to _wip/examples_drawtext/httext.gz diff --git a/examples/drawtext/main.c b/_wip/examples_drawtext/main.c similarity index 100% rename from examples/drawtext/main.c rename to _wip/examples_drawtext/main.c diff --git a/_wip/examples_drawtext_CMakeLists.txt b/_wip/examples_drawtext_CMakeLists.txt new file mode 100644 index 00000000..f2289d21 --- /dev/null +++ b/_wip/examples_drawtext_CMakeLists.txt @@ -0,0 +1,58 @@ +# 3 june 2016 + +if(WIN32) + set(_EXAMPLE_RESOURCES_RC resources.rc) +endif() + +macro(_add_example _name) + _add_exec(${_name} ${ARGN}) + # because Microsoft's toolchain is dumb + if(MSVC) + set_property(TARGET ${_name} APPEND_STRING PROPERTY + LINK_FLAGS " /ENTRY:mainCRTStartup") + endif() +endmacro() + +_add_example(controlgallery + controlgallery/main.c + ${_EXAMPLE_RESOURCES_RC} +) + +_add_example(histogram + histogram/main.c + ${_EXAMPLE_RESOURCES_RC} +) + +_add_example(cpp-multithread + cpp-multithread/main.cpp + ${_EXAMPLE_RESOURCES_RC} +) +if(NOT WIN32) + target_link_libraries(cpp-multithread pthread) +endif() + +_add_example(drawtext + drawtext/attributes.c + drawtext/basic.c + drawtext/emptystr_hittest.c + drawtext/hittest.c + drawtext/main.c + ${_EXAMPLE_RESOURCES_RC} +) +target_include_directories(drawtext + PRIVATE drawtext) + +_add_example(opentype + opentype/main.c + ${_EXAMPLE_RESOURCES_RC} +) +target_include_directories(opentype + PRIVATE opentype) + +add_custom_target(examples + DEPENDS + controlgallery + histogram + cpp-multithread + drawtext + opentype) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f2289d21..b820df8b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,17 +31,6 @@ if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() -_add_example(drawtext - drawtext/attributes.c - drawtext/basic.c - drawtext/emptystr_hittest.c - drawtext/hittest.c - drawtext/main.c - ${_EXAMPLE_RESOURCES_RC} -) -target_include_directories(drawtext - PRIVATE drawtext) - _add_example(opentype opentype/main.c ${_EXAMPLE_RESOURCES_RC} From 427e013d78b1fd9708dae7b4763a746ae29a0475 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:42:50 -0500 Subject: [PATCH 399/487] And moved the OpenType example out of the way too. --- {examples/opentype => _wip/examples_opentype}/main.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {examples/opentype => _wip/examples_opentype}/main.c (100%) diff --git a/examples/opentype/main.c b/_wip/examples_opentype/main.c similarity index 100% rename from examples/opentype/main.c rename to _wip/examples_opentype/main.c From 1fae3eea02de828f281156f899a0f89528004d40 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 21:57:45 -0500 Subject: [PATCH 400/487] And wrote a new, simpler drawtext example. Now to debug run-time issues in the new attributed string code! First up: some infinite loop. --- examples/CMakeLists.txt | 6 +- examples/drawtext/main.c | 158 +++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 1 + 3 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 examples/drawtext/main.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b820df8b..cdedb039 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,12 +31,10 @@ if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() -_add_example(opentype - opentype/main.c +_add_example(drawtext + drawtext/main.c ${_EXAMPLE_RESOURCES_RC} ) -target_include_directories(opentype - PRIVATE opentype) add_custom_target(examples DEPENDS diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c new file mode 100644 index 00000000..13c7c659 --- /dev/null +++ b/examples/drawtext/main.c @@ -0,0 +1,158 @@ +// 10 march 2018 +#include +#include +#include "../../ui.h" + +uiWindow *mainwin; +uiArea *area; +uiAreaHandler handler; +uiFontButton *fontButton; + +uiAttributedString *attrstr; + +#define margins 20 + +static void appendWithAttribute(const char *what, uiAttribute *attr) +{ + size_t start, end; + + start = uiAttributedStringLen(attrstr); + end = start + strlen(what); + uiAttributedStringAppendUnattributed(attrstr, what); + uiAttributedStringSetAttribute(attrstr, attr, start, end); +} + +static void makeAttributedString(void) +{ + uiAttribute *attr; + + attrstr = uiNewAttributedString( + "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout obects.\n" + "uiAttributedString lets you have a variety of attributes: "); + + attr = uiNewFamilyAttribute("Courier New"); + appendWithAttribute("font family", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewSizeAttribute(18); + appendWithAttribute("font size", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewWeightAttribute(uiTextWeightBold); + appendWithAttribute("font weight", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewItalicAttribute(uiTextItalicItalic); + appendWithAttribute("font italicness", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewStretchAttribute(uiTextStretchCondensed); + appendWithAttribute("font stretch", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewColorAttribute(0.75, 0.25, 0.5, 0.75); + appendWithAttribute("text color", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewBackgroundAttribute(0.5, 0.5, 0.25, 0.5); + appendWithAttribute("text background color", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); +} + +static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) +{ + uiDrawTextLayout *textLayout; + uiFontDescriptor fontdesc; + uiDrawTextLayoutParams params; + + params.String = attrstr; + uiFontButtonFont(fontButton, &fontdesc); + params.Width = p->AreaWidth - (2 * margins); + params.Align = uiDrawTextAlignLeft; + textLayout = uiDrawNewTextLayout(¶ms); + // TODO clip to margins + uiDrawText(p->Context, textLayout, margins, margins); + uiDrawFreeTextLayout(textLayout); +} + +static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) +{ + // do nothing +} + +static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) +{ + // do nothing +} + +static void handlerDragBroken(uiAreaHandler *ah, uiArea *a) +{ + // do nothing +} + +static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) +{ + // reject all keys + return 0; +} + +static int onClosing(uiWindow *w, void *data) +{ + uiControlDestroy(uiControl(mainwin)); + uiQuit(); + return 0; +} + +static int shouldQuit(void *data) +{ + uiControlDestroy(uiControl(mainwin)); + return 1; +} + +int main(void) +{ + uiInitOptions o; + const char *err; + uiBox *hbox, *vbox; + + handler.Draw = handlerDraw; + handler.MouseEvent = handlerMouseEvent; + handler.MouseCrossed = handlerMouseCrossed; + handler.DragBroken = handlerDragBroken; + handler.KeyEvent = handlerKeyEvent; + + memset(&o, 0, sizeof (uiInitOptions)); + err = uiInit(&o); + if (err != NULL) { + fprintf(stderr, "error initializing ui: %s\n", err); + uiFreeInitError(err); + return 1; + } + + uiOnShouldQuit(shouldQuit, NULL); + + makeAttributedString(); + + mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); + uiWindowSetMargined(mainwin, 1); + uiWindowOnClosing(mainwin, onClosing, NULL); + + hbox = uiNewHorizontalBox(); + uiBoxSetPadded(hbox, 1); + uiWindowSetChild(mainwin, uiControl(hbox)); + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + uiBoxAppend(hbox, uiControl(vbox), 0); + + fontButton = uiNewFontButton(); + uiBoxAppend(vbox, uiControl(fontButton), 0); + + area = uiNewArea(&handler); + uiBoxAppend(hbox, uiControl(area), 1); + + uiControlShow(uiControl(mainwin)); + uiMain(); + uiUninit(); + return 0; +} diff --git a/ui_attrstr.h b/ui_attrstr.h index 6dd738ee..66b0fb4b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -433,6 +433,7 @@ typedef struct uiDrawTextLayout uiDrawTextLayout; // uiDrawTextAlign specifies the alignment of lines of text in a // uiDrawTextLayout. +// TODO should this really have Draw in the name? _UI_ENUM(uiDrawTextAlign) { uiDrawTextAlignLeft, uiDrawTextAlignCenter, From b15f88412bab17d947347fc968616b33d3131719 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:04:07 -0500 Subject: [PATCH 401/487] Fixed the infinite loop: end is exclusive, and my code was correct in handling that, so <= (which i though was needed there because I thought the code wouldn't handle it) is wrong. Now for a segfault. --- darwin/attrstr.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c53daae7..2c27f12c 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -250,7 +250,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ CFRange range; size_t diff; - while (start <= end) { + while (start < end) { cfa = (uiprivCombinedFontAttr *) CFAttributedStringGetAttribute(p->mas, start, combinedFontAttrName, &range); if (cfa == nil) cfa = [uiprivCombinedFontAttr new]; From 5ef04f26756c8294e9e233ef7945b0915a9f7015 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:16:43 -0500 Subject: [PATCH 402/487] And fixed the other issue: didn't fully set uiDrawTextLayoutParams. Oops =P It works! Also did proper memory management before uiUninit()... but it's crashing for other reasons now (using strdup() instead of uiAlloc()). And Skia doesn't map correctly... --- examples/drawtext/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 13c7c659..fb9ee778 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -62,11 +62,12 @@ static void makeAttributedString(void) static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { uiDrawTextLayout *textLayout; - uiFontDescriptor fontdesc; + uiFontDescriptor defaultFont; uiDrawTextLayoutParams params; params.String = attrstr; - uiFontButtonFont(fontButton, &fontdesc); + uiFontButtonFont(fontButton, &defaultFont); + params.DefaultFont = &defaultFont; params.Width = p->AreaWidth - (2 * margins); params.Align = uiDrawTextAlignLeft; textLayout = uiDrawNewTextLayout(¶ms); @@ -153,6 +154,7 @@ int main(void) uiControlShow(uiControl(mainwin)); uiMain(); + uiFreeAttributedString(attrstr); uiUninit(); return 0; } From 6643a148e019124ca92eac97382edee5279863c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:18:48 -0500 Subject: [PATCH 403/487] More TODOs. --- darwin/fontvariation.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 60a124bd..b5a3591a 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -29,6 +29,9 @@ // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#Types // - https://www.microsoft.com/typography/otspec/avar.htm +// TODO Skia doesn't quite map correctly; notice what passes for condensed in the drawtext example +// TODO also investigate Marker Felt not working right in Thin and Wide modes (but that's probably the other file, putting it here just so I don't forget) + #define fvarWeight 0x77676874 #define fvarWidth 0x77647468 From 6ba2d3606d0f65c9d8cabb42d31854ea491f4c37 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:51:39 -0500 Subject: [PATCH 404/487] Removed uiprivStrdup() (we'll just uiprivAlloc()+strcpy() instead) and fleshed out the drawtext example's text a bit more. --- common/attribute.c | 3 ++- common/attrstr.h | 1 - examples/drawtext/main.c | 50 ++++++++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/common/attribute.c b/common/attribute.c index de0bdc13..5054d1c1 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -85,7 +85,8 @@ uiAttribute *uiNewFamilyAttribute(const char *family) uiAttribute *a; a = newAttribute(uiAttributeTypeFamily); - a->u.family = uiprivStrdup(family); + a->u.family = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)"); + strcpy(a->u.family, family); return a; } diff --git a/common/attrstr.h b/common/attrstr.h index 94fe934f..643c8c3c 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,7 +5,6 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) -#define uiprivStrdup(x) strdup(x) #define uiprivStricmp(x, y) strcasecmp(x, y) // attribute.c diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index fb9ee778..c3b2d968 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -12,7 +12,7 @@ uiAttributedString *attrstr; #define margins 20 -static void appendWithAttribute(const char *what, uiAttribute *attr) +static void appendWithAttribute(const char *what, uiAttribute *attr, uiAttribute *attr2) { size_t start, end; @@ -20,43 +20,75 @@ static void appendWithAttribute(const char *what, uiAttribute *attr) end = start + strlen(what); uiAttributedStringAppendUnattributed(attrstr, what); uiAttributedStringSetAttribute(attrstr, attr, start, end); + if (attr2 != NULL) + uiAttributedStringSetAttribute(attrstr, attr2, start, end); } static void makeAttributedString(void) { - uiAttribute *attr; + uiAttribute *attr, *attr2; + uiOpenTypeFeatures *otf; attrstr = uiNewAttributedString( "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout obects.\n" "uiAttributedString lets you have a variety of attributes: "); attr = uiNewFamilyAttribute("Courier New"); - appendWithAttribute("font family", attr); + appendWithAttribute("font family", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewSizeAttribute(18); - appendWithAttribute("font size", attr); + appendWithAttribute("font size", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewWeightAttribute(uiTextWeightBold); - appendWithAttribute("font weight", attr); + appendWithAttribute("font weight", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewItalicAttribute(uiTextItalicItalic); - appendWithAttribute("font italicness", attr); + appendWithAttribute("font italicness", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewStretchAttribute(uiTextStretchCondensed); - appendWithAttribute("font stretch", attr); + appendWithAttribute("font stretch", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewColorAttribute(0.75, 0.25, 0.5, 0.75); - appendWithAttribute("text color", attr); + appendWithAttribute("text color", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewBackgroundAttribute(0.5, 0.5, 0.25, 0.5); - appendWithAttribute("text background color", attr); + appendWithAttribute("text background color", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); + + + attr = uiNewUnderlineAttribute(uiUnderlineSingle); + appendWithAttribute("underline style", attr, NULL); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + uiAttributedStringAppendUnattributed(attrstr, "and "); + attr = uiNewUnderlineAttribute(uiUnderlineDouble); + attr2 = uiNewUnderlineColorAttribute(uiUnderlineColorCustom, 1.0, 0.0, 0.5, 1.0); + appendWithAttribute("underline color", attr, attr2); + uiAttributedStringAppendUnattributed(attrstr, ". "); + + uiAttributedStringAppendUnattributed(attrstr, "Furthermore, there are attributes allowing for "); + attr = uiNewUnderlineAttribute(uiUnderlineSuggestion); + attr2 = uiNewUnderlineColorAttribute(uiUnderlineColorSpelling, 0, 0, 0, 0); + appendWithAttribute("special underlines for indicating spelling errors", attr, attr2); + uiAttributedStringAppendUnattributed(attrstr, " (and other types of errors) "); + + uiAttributedStringAppendUnattributed(attrstr, "and control over OpenType features such as ligatures (for instance, "); + otf = uiNewOpenTypeFeatures(); + uiOpenTypeFeaturesAdd(otf, 'l', 'i', 'g', 'a', 0); + attr = uiNewFeaturesAttribute(otf); + appendWithAttribute("afford", attr, NULL); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + uiOpenTypeFeaturesAdd(otf, 'l', 'i', 'g', 'a', 1); + attr = uiNewFeaturesAttribute(otf); + appendWithAttribute("afford", attr, NULL); + uiFreeOpenTypeFeatures(otf); + uiAttributedStringAppendUnattributed(attrstr, ")."); } static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) From acbe7c3149947fe07dee3c2ff0f25e7a16c3a676 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 03:26:32 -0400 Subject: [PATCH 405/487] Implemented uiAttributeTypeBackground for OS X 10.12 and newer using the attribute included with Core Text. Also laid out a non-block-based API for drawing backgrounds on older versions of OS X (not implemented here yet, however; that will require bringing back the old metrics code). --- darwin/attrstr.h | 16 +++++++++++-- darwin/attrstr.m | 52 ++++++++++++++++++++++-------------------- darwin/drawtext.m | 35 +++++++++++++++++++++++----- darwin/future.m | 4 ++++ darwin/uipriv_darwin.h | 1 + 5 files changed, 75 insertions(+), 33 deletions(-) diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 384a607b..f77b4d86 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -74,5 +74,17 @@ extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axi // attrstr.m extern void uiprivInitUnderlineColors(void); extern void uiprivUninitUnderlineColors(void); -typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); -extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); +extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams); + +// drawtext.m +@interface uiprivDrawTextBackgroundParams : NSObject { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +} +- (id)initWithStart:(size_t)s end:(size_t)e r:(double)red g:(double)green b:(double)blue a:(double)alpha; +- (void)draw:(CGContextRef)c layout:(uiDrawTextLayout *)layout at:(double)x y:(double)y utf8Mapping:(const size_t *)u16tou8; +@end diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 2c27f12c..530e6a22 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -65,7 +65,7 @@ void uiprivUninitUnderlineColors(void) // TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableArray *backgroundBlocks; + NSMutableArray *backgroundParams; }; // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute @@ -271,20 +271,6 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ } } -static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) -{ - return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { - uiDrawBrush brush; - - brush.Type = uiDrawBrushTypeSolid; - brush.R = r; - brush.G = g; - brush.B = b; - brush.A = a; -//TODO drawTextBackground(c, x, y, layout, start, end, &brush, 0); - }); -} - static CGColorRef mkcolor(double r, double g, double b, double a) { CGColorSpaceRef colorspace; @@ -305,20 +291,38 @@ static CGColorRef mkcolor(double r, double g, double b, double a) return color; } +static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) +{ + uiprivDrawTextBackgroundParams *dtb; + + // TODO make sure this works properly with line paragraph spacings (after figuring out what that means, of course) + if (FUTURE_kCTBackgroundColorAttributeName != NULL) { + CGColorRef color; + CFRange range; + + color = mkcolor(r, g, b, a); + range.location = start; + range.length = end - start; + CFAttributedStringSetAttribute(p->mas, range, *FUTURE_kCTBackgroundColorAttributeName, color); + CFRelease(color); + return; + } + + dtb = [[uiprivDrawTextBackgroundParams alloc] initWithStart:start end:end r:r g:g b:b a:a]; + [p->backgroundParams addObject:dtb]; + [dtb release]; +} + static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; CGColorRef color; - size_t ostart, oend; - backgroundBlock block; int32_t us; CFNumberRef num; double r, g, b, a; uiUnderlineColor colorType; - ostart = start; - oend = end; start = uiprivAttributedStringUTF8ToUTF16(s, start); end = uiprivAttributedStringUTF8ToUTF16(s, end); range.location = start; @@ -340,9 +344,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; case uiAttributeTypeBackground: uiAttributeColor(attr, &r, &g, &b, &a); - block = mkBackgroundBlock(ostart, oend, r, g, b, a); - [p->backgroundBlocks addObject:block]; - Block_release(block); + addBackgroundAttribute(p, start, end, r, g, b, a); break; // TODO turn into a class, like we did with the font attributes, or even integrate *into* the font attributes case uiAttributeTypeUnderline: @@ -453,7 +455,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) } // TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... -CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) +CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; @@ -493,11 +495,11 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou CFAttributedStringBeginEditing(mas); fep.mas = mas; - fep.backgroundBlocks = [NSMutableArray new]; + fep.backgroundParams = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyFontAttributes(mas, p->DefaultFont); CFAttributedStringEndEditing(mas); - *backgroundBlocks = fep.backgroundBlocks; + *backgroundParams = fep.backgroundParams; return mas; } diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 21c6e45e..5406617c 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -8,7 +8,7 @@ // in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within @interface uiprivTextFrame : NSObject { CFAttributedStringRef attrstr; - NSArray *backgroundBlocks; + NSArray *backgroundParams; CTFramesetterRef framesetter; CGSize size; CGPathRef path; @@ -20,6 +20,29 @@ - (CFArrayRef)lines; @end +@implementation uiprivDrawTextBackgroundParams + +- (id)initWithStart:(size_t)s end:(size_t)e r:(double)red g:(double)green b:(double)blue a:(double)alpha +{ + self = [super init]; + if (self) { + self->start = s; + self->end = e; + self->r = red; + self->g = green; + self->b = blue; + self->a = alpha; + } + return self; +} + +- (void)draw:(CGContextRef)c layout:(uiDrawTextLayout *)layout at:(double)x y:(double)y utf8Mapping:(const size_t *)u16tou8 +{ + // TODO +} + +@end + @implementation uiprivTextFrame - (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p @@ -31,7 +54,7 @@ self = [super init]; if (self) { - self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundBlocks)); + self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundParams)); // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing self->framesetter = CTFramesetterCreateWithAttributedString(self->attrstr); if (self->framesetter == NULL) { @@ -71,22 +94,22 @@ CFRelease(self->frame); CFRelease(self->path); CFRelease(self->framesetter); - [self->backgroundBlocks release]; + [self->backgroundParams release]; CFRelease(self->attrstr); [super dealloc]; } - (void)draw:(uiDrawContext *)c textLayout:(uiDrawTextLayout *)tl at:(double)x y:(double)y { - backgroundBlock b; + uiprivDrawTextBackgroundParams *dtb; CGAffineTransform textMatrix; CGContextSaveGState(c->c); // save the text matrix because it's not part of the graphics state textMatrix = CGContextGetTextMatrix(c->c); - for (b in self->backgroundBlocks) - b(c, tl, x, y); + for (dtb in self->backgroundParams) + /* TODO */; // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) diff --git a/darwin/future.m b/darwin/future.m index 60936f40..a262d009 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -8,6 +8,9 @@ CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; +// added in OS X 10.12; we need 10.8 +CFStringRef *FUTURE_kCTBackgroundColorAttributeName = NULL; + // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) void loadFutures(void) { @@ -20,6 +23,7 @@ void loadFutures(void) #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) GET(FUTURE_kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureTag); GET(FUTURE_kCTFontOpenTypeFeatureValue, kCTFontOpenTypeFeatureValue); + GET(FUTURE_kCTBackgroundColorAttributeName, kCTBackgroundColorAttributeName); dlclose(handle); } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0711c68b..8b95e315 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,6 +145,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; +extern CFStringRef *FUTURE_kCTBackgroundColorAttributeName; extern void loadFutures(void); extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); From 008be9b6d8889cbf711b8ebe919cbcdd401fd06e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 15:55:28 -0400 Subject: [PATCH 406/487] Began migrating the Unix draw text code by moving the existing files out of the way. --- unix/{_old_drawtext.c => OLD__old_drawtext.c} | 0 unix/{attrstr.c => OLD_attrstr.c} | 0 unix/{drawtext.c => OLD_drawtext.c} | 0 unix/{fontbutton.c => OLD_fontbutton.c} | 0 unix/{graphemes.c => OLD_graphemes.c} | 0 unix/{opentype.c => OLD_opentype.c} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename unix/{_old_drawtext.c => OLD__old_drawtext.c} (100%) rename unix/{attrstr.c => OLD_attrstr.c} (100%) rename unix/{drawtext.c => OLD_drawtext.c} (100%) rename unix/{fontbutton.c => OLD_fontbutton.c} (100%) rename unix/{graphemes.c => OLD_graphemes.c} (100%) rename unix/{opentype.c => OLD_opentype.c} (100%) diff --git a/unix/_old_drawtext.c b/unix/OLD__old_drawtext.c similarity index 100% rename from unix/_old_drawtext.c rename to unix/OLD__old_drawtext.c diff --git a/unix/attrstr.c b/unix/OLD_attrstr.c similarity index 100% rename from unix/attrstr.c rename to unix/OLD_attrstr.c diff --git a/unix/drawtext.c b/unix/OLD_drawtext.c similarity index 100% rename from unix/drawtext.c rename to unix/OLD_drawtext.c diff --git a/unix/fontbutton.c b/unix/OLD_fontbutton.c similarity index 100% rename from unix/fontbutton.c rename to unix/OLD_fontbutton.c diff --git a/unix/graphemes.c b/unix/OLD_graphemes.c similarity index 100% rename from unix/graphemes.c rename to unix/OLD_graphemes.c diff --git a/unix/opentype.c b/unix/OLD_opentype.c similarity index 100% rename from unix/opentype.c rename to unix/OLD_opentype.c From 1cce6dc70416de048201ecde9513c138adce0afc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 16:15:28 -0400 Subject: [PATCH 407/487] Migrated opentype.c and graphemes.c back. --- unix/OLD_opentype.c | 208 -------------------------- unix/attrstr.h | 4 + unix/{OLD_graphemes.c => graphemes.c} | 5 +- unix/opentype.c | 26 ++++ 4 files changed, 33 insertions(+), 210 deletions(-) delete mode 100644 unix/OLD_opentype.c create mode 100644 unix/attrstr.h rename unix/{OLD_graphemes.c => graphemes.c} (92%) create mode 100644 unix/opentype.c diff --git a/unix/OLD_opentype.c b/unix/OLD_opentype.c deleted file mode 100644 index 30933ea8..00000000 --- a/unix/OLD_opentype.c +++ /dev/null @@ -1,208 +0,0 @@ -// 11 may 2017 -#include "uipriv_unix.h" - -// TODO switch from GINT_TO_POINTER() and so to a fake GUINT_TO_POINTER()? - -struct uiOpenTypeFeatures { - GHashTable *tags; -}; - -uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) -{ - uiOpenTypeFeatures *otf; - - otf = uiNew(uiOpenTypeFeatures); - otf->tags = g_hash_table_new(g_direct_hash, g_direct_equal); - return otf; -} - -void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) -{ - g_hash_table_destroy(otf->tags); - uiFree(otf); -} - -static void cloneTags(gpointer key, gpointer value, gpointer data) -{ - // TODO is there a G_HASH_TABLE()? - g_hash_table_replace((GHashTable *) data, key, value); -} - -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) -{ - uiOpenTypeFeatures *out; - - // TODO switch the windows one to use this - out = uiNewOpenTypeFeatures(); - g_hash_table_foreach(otf->tags, cloneTags, out->tags); - return out; -} - -static gpointer mkTag(char a, char b, char c, char d) -{ - uint32_t tag; - - tag = (((uint32_t) a) & 0xFF) << 24; - tag |= (((uint32_t) b) & 0xFF) << 16; - tag |= (((uint32_t) c) & 0xFF) << 8; - tag |= ((uint32_t) d) & 0xFF; - return GINT_TO_POINTER(tag); -} - -void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) -{ - g_hash_table_replace(otf->tags, mkTag(a, b, c, d), GINT_TO_POINTER(value)); -} - -void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) -{ - // will just return FALSE if the tag is not in otf->tags (half-documented as such), so we can use it safely - g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); -} - -// TODO will the const wreck stuff up? -int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) -{ - gboolean found; - gpointer gv; - - found = g_hash_table_lookup_extended(otf->tags, - mkTag(a, b, c, d), - NULL, &gv); - if (!found) - return 0; - *value = GPOINTER_TO_INT(gv); - return 1; -} - -struct otfForEach { - const uiOpenTypeFeatures *otf; - uiOpenTypeFeaturesForEachFunc f; - void *data; - uiForEach ret; -}; - -static void foreach(gpointer key, gpointer value, gpointer data) -{ - struct otfForEach *ofe = (struct otfForEach *) data; - uint32_t tag; - uint8_t a, b, c, d; - - // we can't stop early, so just ignore the rest if we have to - if (ofe->ret == uiForEachStop) - return; - tag = GPOINTER_TO_INT(key); - a = (uint8_t) ((tag >> 24) & 0xFF); - b = (uint8_t) ((tag >> 16) & 0xFF); - c = (uint8_t) ((tag >> 8) & 0xFF); - d = (uint8_t) (tag & 0xFF); - ofe->ret = (*(ofe->f))(ofe->otf, (char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); -} - -void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) -{ - struct otfForEach ofe; - - memset(&ofe, 0, sizeof (struct otfForEach)); - ofe.otf = otf; - ofe.f = f; - ofe.data = data; - g_hash_table_foreach(otf->tags, foreach, &ofe); -} - -static gint tagcmp(gconstpointer a, gconstpointer b) -{ - return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b); -} - -static GList *copySortedKeys(GHashTable *tags) -{ - GList *k, *copy; - - k = g_hash_table_get_keys(tags); - copy = g_list_copy(k); - copy = g_list_sort(copy, tagcmp); - // TODO do we free k? the docs contradict themselves - // TODO I already forgot, does g_list_sort() copy, or just change the head? - return copy; -} - -int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) -{ - GList *ak, *bk; - GList *ai, *bi; - guint na, nb; - guint i; - int equal = 0; - - if (a == NULL && b == NULL) - return 1; - if (a == NULL || b == NULL) - return 0; - - ak = copySortedKeys(a->tags); - bk = copySortedKeys(b->tags); - - na = g_list_length(ak); - nb = g_list_length(bk); - if (na != nb) { - equal = 0; - goto out; - } - - // TODO use GPOINTER_TO_INT() in these? - ai = ak; - bi = bk; - for (i = 0; i < na; i++) { - gpointer av, bv; - - // compare keys - // this is why we needed to sort earlier - if (ai->data != bi->data) { - equal = 0; - goto out; - } - // and compare values - av = g_hash_table_lookup(a->tags, ai->data); - bv = g_hash_table_lookup(b->tags, bi->data); - if (av != bv) { - equal = 0; - goto out; - } - ai = ai->next; - bi = bi->next; - } - - // all good - equal = 1; - -out: - g_list_free(bk); - g_list_free(ak); - return equal; -} - -// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings -// TODO make this a g_hash_table_foreach() function (which requires duplicating code)? -static uiForEach toCSS(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) -{ - // TODO is there a G_STRING()? - GString *s = (GString *) data; - - // the last trailing comma is removed after foreach is done - g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", - a, b, c, d, value); - return uiForEachContinue; -} - -gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf) -{ - GString *s; - - s = g_string_new(""); - uiOpenTypeFeaturesForEach(otf, toCSS, s); - if (s->len != 0) - // and remove the last comma - g_string_truncate(s, s->len - 2); - return g_string_free(s, FALSE); -} diff --git a/unix/attrstr.h b/unix/attrstr.h new file mode 100644 index 00000000..3c3e49b3 --- /dev/null +++ b/unix/attrstr.h @@ -0,0 +1,4 @@ +// 11 march 2018 + +// opentype.c +extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); diff --git a/unix/OLD_graphemes.c b/unix/graphemes.c similarity index 92% rename from unix/OLD_graphemes.c rename to unix/graphemes.c index 4f957352..296e655f 100644 --- a/unix/OLD_graphemes.c +++ b/unix/graphemes.c @@ -1,12 +1,13 @@ // 25 may 2016 #include "uipriv_unix.h" +#include "attrstr.h" -int graphemesTakesUTF16(void) +int uiprivGraphemesTakesUTF16(void) { return 0; } -struct graphemes *graphemes(void *s, size_t len) +struct graphemes *uiprivNewGraphemes(void *s, size_t len) { struct graphemes *g; char *text = (char *) s; diff --git a/unix/opentype.c b/unix/opentype.c new file mode 100644 index 00000000..281acda7 --- /dev/null +++ b/unix/opentype.c @@ -0,0 +1,26 @@ +// 11 may 2017 +#include "uipriv_unix.h" +#include "attrstr.h" + +// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings +static uiForEach toCSS(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + GString *s = (GString *) data; + + // the last trailing comma is removed after foreach is done + g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", + a, b, c, d, value); + return uiForEachContinue; +} + +GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf) +{ + GString *s; + + s = g_string_new(""); + uiOpenTypeFeaturesForEach(otf, toCSS, s); + if (s->len != 0) + // and remove the last comma + g_string_truncate(s, s->len - 2); + return s; +} From 7dc5c6d940800cfb3b9e29891909ec8d510a605a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 17:36:22 -0400 Subject: [PATCH 408/487] Migrated attrstr.c back. --- darwin/attrstr.m | 2 +- unix/OLD_attrstr.c | 211 --------------------------------------------- unix/attrstr.c | 173 +++++++++++++++++++++++++++++++++++++ unix/attrstr.h | 16 ++++ 4 files changed, 190 insertions(+), 212 deletions(-) delete mode 100644 unix/OLD_attrstr.c create mode 100644 unix/attrstr.c diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 530e6a22..180bf272 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -454,7 +454,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } -// TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... +// TODO either rename this (on all platforms) to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams) { CFStringRef cfstr; diff --git a/unix/OLD_attrstr.c b/unix/OLD_attrstr.c deleted file mode 100644 index 36141b7c..00000000 --- a/unix/OLD_attrstr.c +++ /dev/null @@ -1,211 +0,0 @@ -// 12 february 2017 -#include "uipriv_unix.h" - -// TODO pango alpha attributes turn 0 into 65535 :| - -// TODO make this name less generic? -struct foreachParams { - PangoAttrList *attrs; - // TODO use pango's built-in background attribute? - GPtrArray *backgroundClosures; -}; - -struct closureParams { - size_t start; - size_t end; - double r; - double g; - double b; - double a; -}; - -static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) -{ - struct closureParams *p = (struct closureParams *) data; - uiDrawBrush brush; - - brush.Type = uiDrawBrushTypeSolid; - brush.R = p->r; - brush.G = p->g; - brush.B = p->b; - brush.A = p->a; - drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); -} - -static void freeClosureParams(gpointer data, GClosure *closure) -{ - uiFree((struct closureParams *) data); -} - -static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) -{ - struct closureParams *p; - GClosure *closure; - - p = uiNew(struct closureParams); - p->start = start; - p->end = end; - p->r = r; - p->g = g; - p->b = b; - p->a = a; - closure = g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); - // TODO write a specific marshaler - // TODO or drop the closure stuff entirely - g_closure_set_marshal(closure, g_cclosure_marshal_generic); - return closure; -} - -static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) -{ - if (attr == NULL) // in case of a future attribute - return; - attr->start_index = start; - attr->end_index = end; - pango_attr_list_insert(p->attrs, attr); -} - -static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) -{ - struct foreachParams *p = (struct foreachParams *) data; - GClosure *closure; - PangoUnderline underline; - char *featurestr; - - switch (spec->Type) { - case uiAttributeFamily: - addattr(p, start, end, - pango_attr_family_new(spec->Family)); - break; - case uiAttributeSize: - addattr(p, start, end, - pango_attr_size_new(cairoToPango(spec->Double))); - break; - case uiAttributeWeight: - // TODO reverse the misalignment from drawtext.c if it is corrected - addattr(p, start, end, - pango_attr_weight_new((PangoWeight) (spec->Value))); - break; - case uiAttributeItalic: - addattr(p, start, end, - pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); - break; - case uiAttributeStretch: - addattr(p, start, end, - pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); - break; - case uiAttributeColor: - addattr(p, start, end, - pango_attr_foreground_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); - addattr(p, start, end, - FUTURE_pango_attr_foreground_alpha_new( - (guint16) (spec->A * 65535.0))); - break; - case uiAttributeBackground: - closure = mkBackgroundClosure(start, end, - spec->R, spec->G, spec->B, spec->A); - g_ptr_array_add(p->backgroundClosures, closure); - break; - case uiAttributeUnderline: - switch (spec->Value) { - case uiDrawUnderlineStyleNone: - underline = PANGO_UNDERLINE_NONE; - break; - case uiDrawUnderlineStyleSingle: - underline = PANGO_UNDERLINE_SINGLE; - break; - case uiDrawUnderlineStyleDouble: - underline = PANGO_UNDERLINE_DOUBLE; - break; - case uiDrawUnderlineStyleSuggestion: - underline = PANGO_UNDERLINE_ERROR; - break; - } - addattr(p, start, end, - pango_attr_underline_new(underline)); - break; - case uiAttributeUnderlineColor: - switch (spec->Value) { - case uiDrawUnderlineColorCustom: - addattr(p, start, end, - pango_attr_underline_color_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); - break; - case uiDrawUnderlineColorSpelling: - // TODO GtkTextView style property error-underline-color - addattr(p, start, end, - pango_attr_underline_color_new(65535, 0, 0)); - break; - case uiDrawUnderlineColorGrammar: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 65535, 0)); - break; - case uiDrawUnderlineColorAuxiliary: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 0, 65535)); - break; - } - break; - case uiAttributeFeatures: - // only generate an attribute if spec->Features is not NULL - // TODO state that this is allowed - if (spec->Features == NULL) - break; - featurestr = otfToPangoCSSString(spec->Features); - addattr(p, start, end, - FUTURE_pango_attr_font_features_new(featurestr)); - g_free(featurestr); - break; - default: - // TODO complain - ; - } - return uiForEachContinue; -} - -static void unrefClosure(gpointer data) -{ - g_closure_unref((GClosure *) data); -} - -PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures) -{ - struct foreachParams fep; - - fep.attrs = pango_attr_list_new(); - fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); - uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - *backgroundClosures = fep.backgroundClosures; - return fep.attrs; -} - -void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) -{ - GValue values[4] = { - // the zero-initialization is needed for g_value_init() to work - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - }; - - g_value_init(values + 0, G_TYPE_POINTER); - g_value_set_pointer(values + 0, c); - g_value_init(values + 1, G_TYPE_POINTER); - g_value_set_pointer(values + 1, layout); - g_value_init(values + 2, G_TYPE_DOUBLE); - g_value_set_double(values + 2, x); - g_value_init(values + 3, G_TYPE_DOUBLE); - g_value_set_double(values + 3, y); - g_closure_invoke(closure, - NULL, - 4, values, - NULL); -} diff --git a/unix/attrstr.c b/unix/attrstr.c new file mode 100644 index 00000000..0ffb0b56 --- /dev/null +++ b/unix/attrstr.c @@ -0,0 +1,173 @@ +// 12 february 2017 +#include "uipriv_unix.h" +#include "attrstr.h" + +// TODO pango alpha attributes turn 0 into 65535 :| + +// TODO make this name less generic? +struct foreachParams { + PangoAttrList *attrs; + // TODO use pango's built-in background attribute + GPtrArray *backgroundParams; +}; + +static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +{ + struct closureParams *p = (struct closureParams *) data; + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = p->r; + brush.G = p->g; + brush.B = p->b; + brush.A = p->a; +//TODO drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); +} + +static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) +{ + uiprivDrawTextBackgroundParams *dtb; + + dtb = uiprivNew(uiprivDrawTextBackgroundParams); + dtb->start = start; + dtb->end = end; + dtb->r = r; + dtb->g = g; + dtb->b = b; + dtb->a = a; + g_ptr_array_add(p->backgroundParams, dtb); +} + +static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) +{ + if (attr == NULL) // in case of a future attribute + return; + attr->start_index = start; + attr->end_index = end; + pango_attr_list_insert(p->attrs, attr); +} + +static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; + double r, g, b, a; + PangoUnderline underline; + uiUnderlineColor colorType; + const uiOpenTypeFeatures *features; + GString *featurestr; + + switch (uiAttributeGetType(attr)) { + case uiAttributeTypeFamily: + addattr(p, start, end, + pango_attr_family_new(uiAttributeFamily(attr))); + break; + case uiAttributeTypeSize: + addattr(p, start, end, + pango_attr_size_new(cairoToPango(uiAttributeSize(attr)))); + break; + case uiAttributeTypeWeight: + // TODO reverse the misalignment from drawtext.c if it is corrected + addattr(p, start, end, + pango_attr_weight_new((PangoWeight) uiAttributeWeight(attr))); + break; + case uiAttributeTypeItalic: + addattr(p, start, end, + pango_attr_style_new(pangoItalics[uiAttributeItalic(attr)])); + break; + case uiAttributeTypeStretch: + addattr(p, start, end, + pango_attr_stretch_new(pangoStretches[uiAttributeStretch(attr)])); + break; + case uiAttributeTypeColor: + uiAttributeColor(attr, &r, &g, &b, &a); + addattr(p, start, end, + pango_attr_foreground_new( + (guint16) (r * 65535.0), + (guint16) (g * 65535.0), + (guint16) (b * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_foreground_alpha_new( + (guint16) (a * 65535.0))); + break; + case uiAttributeTypeBackground: + uiAttributeColor(attr, &r, &g, &b, &a); + addBackgroundAttribute(p, start, end, r, g, b, a); + break; + case uiAttributeTypeUnderline: + switch (uiAttributeUnderline(attr)) { + case uiUnderlineNone: + underline = PANGO_UNDERLINE_NONE; + break; + case uiUnderlineSingle: + underline = PANGO_UNDERLINE_SINGLE; + break; + case uiUnderlineDouble: + underline = PANGO_UNDERLINE_DOUBLE; + break; + case uiUnderlineSuggestion: + underline = PANGO_UNDERLINE_ERROR; + break; + } + addattr(p, start, end, + pango_attr_underline_new(underline)); + break; + case uiAttributeTypeUnderlineColor: + uiAttributeUnderlineColor(attr, &colorType, &r, &g, &b, &a); + switch (colorType) { + case uiUnderlineColorCustom: + addattr(p, start, end, + pango_attr_underline_color_new( + (guint16) (r * 65535.0), + (guint16) (g * 65535.0), + (guint16) (b * 65535.0))); + break; + case uiUnderlineColorSpelling: + // TODO GtkTextView style property error-underline-color + addattr(p, start, end, + pango_attr_underline_color_new(65535, 0, 0)); + break; + case uiUnderlineColorGrammar: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 65535, 0)); + break; + case uiUnderlineColorAuxiliary: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 0, 65535)); + break; + } + break; + case uiAttributeTypeFeatures: + // only generate an attribute if the features object is not NULL + // TODO state that this is allowed + features = uiAttributeFeatures(attr); + if (features == NULL) + break; + featurestr = uiprivOpenTypeFeaturesToPangoCSSFeaturesString(features); + addattr(p, start, end, + FUTURE_pango_attr_font_features_new(featurestr->str)); + g_string_free(featurestr, TRUE); + break; + default: + // TODO complain + ; + } + return uiForEachContinue; +} + +static void freeBackgroundParams(gpointer data) +{ + uiprivFree((uiprivDrawTextBackgroundParams *) data); +} + +PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams) +{ + struct foreachParams fep; + + fep.attrs = pango_attr_list_new(); + fep.backgroundParams = g_ptr_array_new_with_free_func(freeBackgroundParams); + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + *backgroundParams = fep.backgroundParams; + return fep.attrs; +} diff --git a/unix/attrstr.h b/unix/attrstr.h index 3c3e49b3..c2632786 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -1,4 +1,20 @@ // 11 march 2018 +#import "../common/attrstr.h" // opentype.c extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); + +// attrstr.c +extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); + +// drawtext.c +// TODO figure out where this type should *really* go in all the headers... +typedef struct uiprivDrawTextBackgroundParams uiprivDrawTextBackgroundParams; +struct uiprivDrawTextBackgroundParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; From 24d2220fe5f2ff5b2fef67cb469371fbc71921c3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 18:12:15 -0400 Subject: [PATCH 409/487] Migrated drawtext.c. --- unix/OLD_drawtext.c | 82 --------------------------- unix/attrstr.h | 8 +++ unix/drawtext.c | 132 ++++++++++++++++++++++++++++++++++++++++++++ unix/uipriv_unix.h | 13 ----- 4 files changed, 140 insertions(+), 95 deletions(-) create mode 100644 unix/drawtext.c diff --git a/unix/OLD_drawtext.c b/unix/OLD_drawtext.c index 373702cd..0626b6be 100644 --- a/unix/OLD_drawtext.c +++ b/unix/OLD_drawtext.c @@ -15,35 +15,6 @@ struct uiDrawTextLayout { int nLines; }; -// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description -// For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double -#define pangoToCairo(pango) (pango_units_to_double(pango)) -// cairoToPango() is in uipriv_unix.h because attrstr.c needs it - -// we need a context for a few things -// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent -// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings -// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us -#define mkGenericPangoCairoContext() (gdk_pango_context_get()) - -const PangoStyle pangoItalics[] = { - [uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL, - [uiDrawTextItalicOblique] = PANGO_STYLE_OBLIQUE, - [uiDrawTextItalicItalic] = PANGO_STYLE_ITALIC, -}; - -const PangoStretch pangoStretches[] = { - [uiDrawTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, - [uiDrawTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, - [uiDrawTextStretchCondensed] = PANGO_STRETCH_CONDENSED, - [uiDrawTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, - [uiDrawTextStretchNormal] = PANGO_STRETCH_NORMAL, - [uiDrawTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, - [uiDrawTextStretchExpanded] = PANGO_STRETCH_EXPANDED, - [uiDrawTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, - [uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, -}; - // TODO neither these nor the overall extents seem to include trailing whitespace... we need to figure that out too static void computeLineMetrics(uiDrawTextLayout *tl) { @@ -94,12 +65,6 @@ static void computeLineMetrics(uiDrawTextLayout *tl) pango_layout_iter_free(iter); } -static const PangoAlignment pangoAligns[] = { - [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, - [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, - [uiDrawTextAlignRight] = PANGO_ALIGN_RIGHT, -}; - uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; @@ -152,53 +117,6 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) return tl; } -void uiDrawFreeTextLayout(uiDrawTextLayout *tl) -{ - uiFree(tl->lineMetrics); - g_ptr_array_unref(tl->backgroundClosures); - g_object_unref(tl->layout); - uiFree(tl); -} - -void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) -{ - guint i; - GClosure *closure; - - for (i = 0; i < tl->backgroundClosures->len; i++) { - closure = (GClosure *) g_ptr_array_index(tl->backgroundClosures, i); - invokeBackgroundClosure(closure, c, tl, x, y); - } - // TODO have an implicit save/restore on each drawing functions instead? and is this correct? - cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); - cairo_move_to(c->cr, x, y); - pango_cairo_show_layout(c->cr, tl->layout); -} - -void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) -{ - PangoRectangle logical; - - pango_layout_get_extents(tl->layout, NULL, &logical); - *width = pangoToCairo(logical.width); - *height = pangoToCairo(logical.height); -} - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ - return pango_layout_get_line_count(tl->layout); -} - -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ - PangoLayoutLine *pll; - - pll = pango_layout_get_line_readonly(tl->layout, line); - *start = pll->start_index; - *end = pll->start_index + pll->length; - // TODO unref pll? -} - void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { *m = tl->lineMetrics[line]; diff --git a/unix/attrstr.h b/unix/attrstr.h index c2632786..53acc496 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -1,6 +1,11 @@ // 11 march 2018 #import "../common/attrstr.h" +// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description +// For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double +#define pangoToCairo(pango) (pango_units_to_double(pango)) +#define cairoToPango(cairo) (pango_units_from_double(cairo)) + // opentype.c extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); @@ -18,3 +23,6 @@ struct uiprivDrawTextBackgroundParams { double b; double a; }; +// TODO move this to a fontmatch.c +extern const PangoStyle uiprivPangoItalics[]; +extern const PangoStretch uiprivPangoStretches[]; diff --git a/unix/drawtext.c b/unix/drawtext.c new file mode 100644 index 00000000..d0866929 --- /dev/null +++ b/unix/drawtext.c @@ -0,0 +1,132 @@ +xx 11 march 2018 +#import "uipriv_unix.h" +#import "draw.h" +#import "attrstr.h" + +struct uiDrawTextLayout { + PangoLayout *layout; + GPtrArray *backgroundParams; +}; + +// we need a context for a few things +// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent +// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings +// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us +#define mkGenericPangoCairoContext() (gdk_pango_context_get()) + +const PangoStyle uiprivPangoItalics[] = { + [uiTextItalicNormal] = PANGO_STYLE_NORMAL, + [uiTextItalicOblique] = PANGO_STYLE_OBLIQUE, + [uiTextItalicItalic] = PANGO_STYLE_ITALIC, +}; + +const PangoStretch uiprivPangoStretches[] = { + [uiTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, + [uiTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, + [uiTextStretchCondensed] = PANGO_STRETCH_CONDENSED, + [uiTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, + [uiTextStretchNormal] = PANGO_STRETCH_NORMAL, + [uiTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, + [uiTextStretchExpanded] = PANGO_STRETCH_EXPANDED, + [uiTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, + [uiTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, +}; + +static const PangoAlignment pangoAligns[] = { + [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, + [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, + [uiDrawTextAlignRight] = PANGO_ALIGN_RIGHT, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) +{ + uiDrawTextLayout *tl; + PangoContext *context; + PangoFontDescription *desc; + PangoAttrList *attrs; + int pangoWidth; + + tl = uiprivNew(uiDrawTextLayout); + + // in this case, the context is necessary to create the layout + // the layout takes a ref on the context so we can unref it afterward + context = mkGenericPangoCairoContext(); + tl->layout = pango_layout_new(context); + g_object_unref(context); + + // this is safe; pango_layout_set_text() copies the string + pango_layout_set_text(tl->layout, uiAttributedStringString(p->String), -1); + + desc = pango_font_description_new(); + pango_font_description_set_family(desc, p->DefaultFont->Family); + pango_font_description_set_style(desc, uiprivPangoItalics[p->DefaultFont->Italic]); + // for the most part, pango weights correlate to ours + // the differences: + // - Book — libui: 350, Pango: 380 + // - Ultra Heavy — libui: 950, Pango: 1000 + // TODO figure out what to do about this misalignment + pango_font_description_set_weight(desc, p->DefaultFont->Weight); + pango_font_description_set_stretch(desc, uiprivPangoStretches[p->DefaultFont->Stretch]); + // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double + pango_font_description_set_size(desc, pango_units_from_double(p->DefaultFont->Size)); + pango_layout_set_font_description(tl->layout, desc); + // this is safe; the description is copied + pango_font_description_free(desc); + + pangoWidth = cairoToPango(p->Width); + if (p->Width < 0) + pangoWidth = -1; + pango_layout_set_width(tl->layout, pangoWidth); + + pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); + + attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundFeatures)); + pango_layout_set_attributes(tl->layout, attrs); + pango_attr_list_unref(attrs); + + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + g_ptr_array_unref(tl->backgroundFeatures); + g_object_unref(tl->layout); + uiprivFree(tl); +} + +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + guint i; + + for (i = 0; i < tl->backgroundFeatures->len; i++) { + // TODO + } + // TODO have an implicit save/restore on each drawing functions instead? and is this correct? + cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); + cairo_move_to(c->cr, x, y); + pango_cairo_show_layout(c->cr, tl->layout); +} + +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + PangoRectangle logical; + + pango_layout_get_extents(tl->layout, NULL, &logical); + *width = pangoToCairo(logical.width); + *height = pangoToCairo(logical.height); +} + +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return pango_layout_get_line_count(tl->layout); +} + +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + *start = pll->start_index; + *end = pll->start_index + pll->length; + // TODO unref pll? +} diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 46a2426f..531e1c05 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -61,16 +61,3 @@ extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, // drawtext.c extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); - -// attrstr.c -extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures); -extern void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); - -// drawtext.c -// TODO get rid of these (for attrstr.c) -#define cairoToPango(cairo) (pango_units_from_double(cairo)) -extern const PangoStyle pangoItalics[]; -extern const PangoStretch pangoStretches[]; - -// opentype.c -extern gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf); From 5939c3203d50dba8f7cd60a798fa818f5953a444 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 19:32:08 -0400 Subject: [PATCH 410/487] Created a new file for the font matching code. --- unix/CMakeLists.txt | 1 + unix/attrstr.c | 6 ++--- unix/attrstr.h | 9 +++++--- unix/drawtext.c | 33 ++------------------------- unix/fontmatch.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 37 deletions(-) create mode 100644 unix/fontmatch.c diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index c20cf266..eba09ad9 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -24,6 +24,7 @@ list(APPEND _LIBUI_SOURCES unix/editablecombo.c unix/entry.c unix/fontbutton.c + unix/fontmatch.c unix/form.c unix/future.c unix/graphemes.c diff --git a/unix/attrstr.c b/unix/attrstr.c index 0ffb0b56..0fc9de89 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -68,15 +68,15 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute case uiAttributeTypeWeight: // TODO reverse the misalignment from drawtext.c if it is corrected addattr(p, start, end, - pango_attr_weight_new((PangoWeight) uiAttributeWeight(attr))); + pango_attr_weight_new(uiprivWeightToPangoWeight(uiAttributeWeight(attr)))); break; case uiAttributeTypeItalic: addattr(p, start, end, - pango_attr_style_new(pangoItalics[uiAttributeItalic(attr)])); + pango_attr_style_new(uiprivItalicToPangoStyle(uiAttributeItalic(attr)))); break; case uiAttributeTypeStretch: addattr(p, start, end, - pango_attr_stretch_new(pangoStretches[uiAttributeStretch(attr)])); + pango_attr_stretch_new(uiprivStretchToPangoStretch(uiAttributeStretch(attr)))); break; case uiAttributeTypeColor: uiAttributeColor(attr, &r, &g, &b, &a); diff --git a/unix/attrstr.h b/unix/attrstr.h index 53acc496..5fd072c2 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -9,6 +9,12 @@ // opentype.c extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); +// fontmatch.c +extern PangoWeight uiprivWeightToPangoWeight(uiTextWeight w); +extern PangoStyle uiprivItalicToPangoStyle(uiTextItalic i); +extern PangoStretch uiprivStretchToPangoStretch(uiTextStretch s); +extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc); + // attrstr.c extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); @@ -23,6 +29,3 @@ struct uiprivDrawTextBackgroundParams { double b; double a; }; -// TODO move this to a fontmatch.c -extern const PangoStyle uiprivPangoItalics[]; -extern const PangoStretch uiprivPangoStretches[]; diff --git a/unix/drawtext.c b/unix/drawtext.c index d0866929..47d4362d 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -1,4 +1,4 @@ -xx 11 march 2018 +// 11 march 2018 #import "uipriv_unix.h" #import "draw.h" #import "attrstr.h" @@ -14,24 +14,6 @@ struct uiDrawTextLayout { // so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us #define mkGenericPangoCairoContext() (gdk_pango_context_get()) -const PangoStyle uiprivPangoItalics[] = { - [uiTextItalicNormal] = PANGO_STYLE_NORMAL, - [uiTextItalicOblique] = PANGO_STYLE_OBLIQUE, - [uiTextItalicItalic] = PANGO_STYLE_ITALIC, -}; - -const PangoStretch uiprivPangoStretches[] = { - [uiTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, - [uiTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, - [uiTextStretchCondensed] = PANGO_STRETCH_CONDENSED, - [uiTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, - [uiTextStretchNormal] = PANGO_STRETCH_NORMAL, - [uiTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, - [uiTextStretchExpanded] = PANGO_STRETCH_EXPANDED, - [uiTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, - [uiTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, -}; - static const PangoAlignment pangoAligns[] = { [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, @@ -57,18 +39,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) // this is safe; pango_layout_set_text() copies the string pango_layout_set_text(tl->layout, uiAttributedStringString(p->String), -1); - desc = pango_font_description_new(); - pango_font_description_set_family(desc, p->DefaultFont->Family); - pango_font_description_set_style(desc, uiprivPangoItalics[p->DefaultFont->Italic]); - // for the most part, pango weights correlate to ours - // the differences: - // - Book — libui: 350, Pango: 380 - // - Ultra Heavy — libui: 950, Pango: 1000 - // TODO figure out what to do about this misalignment - pango_font_description_set_weight(desc, p->DefaultFont->Weight); - pango_font_description_set_stretch(desc, uiprivPangoStretches[p->DefaultFont->Stretch]); - // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double - pango_font_description_set_size(desc, pango_units_from_double(p->DefaultFont->Size)); + desc = uiprivFontDescriptorToPangoFontDescription(p->DefaultFont); pango_layout_set_font_description(tl->layout, desc); // this is safe; the description is copied pango_font_description_free(desc); diff --git a/unix/fontmatch.c b/unix/fontmatch.c new file mode 100644 index 00000000..05523c24 --- /dev/null +++ b/unix/fontmatch.c @@ -0,0 +1,55 @@ +// 11 march 2018 +#include "uipriv_unix.h" +#include "attrstr.h" + +static const PangoStyle pangoItalics[] = { + [uiTextItalicNormal] = PANGO_STYLE_NORMAL, + [uiTextItalicOblique] = PANGO_STYLE_OBLIQUE, + [uiTextItalicItalic] = PANGO_STYLE_ITALIC, +}; + +static const PangoStretch pangoStretches[] = { + [uiTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, + [uiTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, + [uiTextStretchCondensed] = PANGO_STRETCH_CONDENSED, + [uiTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, + [uiTextStretchNormal] = PANGO_STRETCH_NORMAL, + [uiTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, + [uiTextStretchExpanded] = PANGO_STRETCH_EXPANDED, + [uiTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, + [uiTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, +}; + +// for the most part, pango weights correlate to ours +// the differences: +// - Book — libui: 350, Pango: 380 +// - Ultra Heavy — libui: 950, Pango: 1000 +// TODO figure out what to do about this misalignment +PangoWeight uiprivWeightToPangoWeight(uiTextWeight w) +{ + return (PangoWeight) w; +} + +PangoStyle uiprivItalicToPangoStyle(uiTextItalic i) +{ + return pangoItalics[i]; +} + +PangoStretch uiprivStretchToPangoStretch(uiTextStretch s) +{ + return pangoStretches[s]; +} + +PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc) +{ + PangoFontDescriptor *desc; + + desc = pango_font_description_new(); + pango_font_description_set_family(desc, uidesc->Family); + // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double + pango_font_description_set_size(desc, pango_units_from_double(uidesc->Size)); + pango_font_description_set_weight(desc, uiprivWeightToPangoWeight(uidesc->Weight)); + pango_font_description_set_style(desc, uiprivItalicToPangoStyle(uidesc->Italic)); + pango_font_description_set_stretch(desc, uiprivStretchToPangoStretch(uidesc->Stretch)); + return desc; +} From 697c926c92893b833651db667ab8ae862ec4f765 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 19:37:30 -0400 Subject: [PATCH 411/487] And migrated fontmatch.c back. Let's test. --- unix/OLD_drawtext.c | 22 ---------------------- unix/attrstr.h | 1 + unix/{OLD_fontbutton.c => fontbutton.c} | 5 +++-- unix/fontmatch.c | 21 +++++++++++++++++++++ 4 files changed, 25 insertions(+), 24 deletions(-) rename unix/{OLD_fontbutton.c => fontbutton.c} (92%) diff --git a/unix/OLD_drawtext.c b/unix/OLD_drawtext.c index 0626b6be..dbd2d709 100644 --- a/unix/OLD_drawtext.c +++ b/unix/OLD_drawtext.c @@ -225,25 +225,3 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) p->xoff = xoff; p->width = width; } - -// TODO split this and the other font description stuff into their own file? -void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) -{ - PangoStyle pitalic; - PangoStretch pstretch; - - uidesc->Family = uiUnixStrdupText(pango_font_description_get_family(pdesc)); - pitalic = pango_font_description_get_style(pdesc); - // TODO reverse the above misalignment if it is corrected - uidesc->Weight = pango_font_description_get_weight(pdesc); - pstretch = pango_font_description_get_stretch(pdesc); - // absolute size does not matter because, as above, 1 device unit == 1 cairo point - uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); - - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) - if (pangoItalics[uidesc->Italic] == pitalic) - break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) - if (pangoStretches[uidesc->Stretch] == pstretch) - break; -} diff --git a/unix/attrstr.h b/unix/attrstr.h index 5fd072c2..1988fe86 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -14,6 +14,7 @@ extern PangoWeight uiprivWeightToPangoWeight(uiTextWeight w); extern PangoStyle uiprivItalicToPangoStyle(uiTextItalic i); extern PangoStretch uiprivStretchToPangoStretch(uiTextStretch s); extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); // attrstr.c extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); diff --git a/unix/OLD_fontbutton.c b/unix/fontbutton.c similarity index 92% rename from unix/OLD_fontbutton.c rename to unix/fontbutton.c index 9a2552b1..3b4a0425 100644 --- a/unix/OLD_fontbutton.c +++ b/unix/fontbutton.c @@ -1,5 +1,6 @@ // 14 april 2016 #include "uipriv_unix.h" +#include "attrstr.h" struct uiFontButton { uiUnixControl c; @@ -31,8 +32,8 @@ void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) PangoFontDescription *pdesc; pdesc = gtk_font_chooser_get_font_desc(b->fc); - fontdescFromPangoFontDescription(pdesc, desc); - // desc is transfer-full and thus is a copy + uiprivFontDescriptorFromPangoFontDescription(pdesc, desc); + // pdesc is transfer-full and thus is a copy pango_font_description_free(pdesc); } diff --git a/unix/fontmatch.c b/unix/fontmatch.c index 05523c24..c55c77a2 100644 --- a/unix/fontmatch.c +++ b/unix/fontmatch.c @@ -53,3 +53,24 @@ PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDes pango_font_description_set_stretch(desc, uiprivStretchToPangoStretch(uidesc->Stretch)); return desc; } + +void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) +{ + PangoStyle pitalic; + PangoStretch pstretch; + + uidesc->Family = uiUnixStrdupText(pango_font_description_get_family(pdesc)); + pitalic = pango_font_description_get_style(pdesc); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = pango_font_description_get_weight(pdesc); + pstretch = pango_font_description_get_stretch(pdesc); + // absolute size does not matter because, as above, 1 device unit == 1 cairo point + uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (pangoItalics[uidesc->Italic] == pitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (pangoStretches[uidesc->Stretch] == pstretch) + break; +} From 602060a673e6c34a3f31951cb6281071c20b643a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 19:59:11 -0400 Subject: [PATCH 412/487] Fixed build errors, some of which were overisghts and others were habits from the OS X code. --- unix/attrstr.c | 13 ------------- unix/attrstr.h | 4 ++-- unix/drawtext.c | 12 ++++++------ unix/fontbutton.c | 2 +- unix/fontmatch.c | 8 ++++---- unix/uipriv_unix.h | 3 --- 6 files changed, 13 insertions(+), 29 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 0fc9de89..fe554c48 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -11,19 +11,6 @@ struct foreachParams { GPtrArray *backgroundParams; }; -static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) -{ - struct closureParams *p = (struct closureParams *) data; - uiDrawBrush brush; - - brush.Type = uiDrawBrushTypeSolid; - brush.R = p->r; - brush.G = p->g; - brush.B = p->b; - brush.A = p->a; -//TODO drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); -} - static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) { uiprivDrawTextBackgroundParams *dtb; diff --git a/unix/attrstr.h b/unix/attrstr.h index 1988fe86..5dff27b3 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -1,5 +1,5 @@ // 11 march 2018 -#import "../common/attrstr.h" +#include "../common/attrstr.h" // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description // For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double @@ -14,7 +14,7 @@ extern PangoWeight uiprivWeightToPangoWeight(uiTextWeight w); extern PangoStyle uiprivItalicToPangoStyle(uiTextItalic i); extern PangoStretch uiprivStretchToPangoStretch(uiTextStretch s); extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc); -extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiFontDescriptor *uidesc); // attrstr.c extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); diff --git a/unix/drawtext.c b/unix/drawtext.c index 47d4362d..b64a9999 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -1,7 +1,7 @@ // 11 march 2018 -#import "uipriv_unix.h" -#import "draw.h" -#import "attrstr.h" +#include "uipriv_unix.h" +#include "draw.h" +#include "attrstr.h" struct uiDrawTextLayout { PangoLayout *layout; @@ -51,7 +51,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundFeatures)); + attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundParams)); pango_layout_set_attributes(tl->layout, attrs); pango_attr_list_unref(attrs); @@ -60,7 +60,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - g_ptr_array_unref(tl->backgroundFeatures); + g_ptr_array_unref(tl->backgroundParams); g_object_unref(tl->layout); uiprivFree(tl); } @@ -69,7 +69,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { guint i; - for (i = 0; i < tl->backgroundFeatures->len; i++) { + for (i = 0; i < tl->backgroundParams->len; i++) { // TODO } // TODO have an implicit save/restore on each drawing functions instead? and is this correct? diff --git a/unix/fontbutton.c b/unix/fontbutton.c index 3b4a0425..b19cd310 100644 --- a/unix/fontbutton.c +++ b/unix/fontbutton.c @@ -27,7 +27,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) +void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc) { PangoFontDescription *pdesc; diff --git a/unix/fontmatch.c b/unix/fontmatch.c index c55c77a2..95f4fdd7 100644 --- a/unix/fontmatch.c +++ b/unix/fontmatch.c @@ -42,7 +42,7 @@ PangoStretch uiprivStretchToPangoStretch(uiTextStretch s) PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc) { - PangoFontDescriptor *desc; + PangoFontDescription *desc; desc = pango_font_description_new(); pango_font_description_set_family(desc, uidesc->Family); @@ -54,7 +54,7 @@ PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDes return desc; } -void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) +void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiFontDescriptor *uidesc) { PangoStyle pitalic; PangoStretch pstretch; @@ -67,10 +67,10 @@ void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, u // absolute size does not matter because, as above, 1 device unit == 1 cairo point uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + for (uidesc->Italic = uiTextItalicNormal; uidesc->Italic < uiTextItalicItalic; uidesc->Italic++) if (pangoItalics[uidesc->Italic] == pitalic) break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + for (uidesc->Stretch = uiTextStretchUltraCondensed; uidesc->Stretch < uiTextStretchUltraExpanded; uidesc->Stretch++) if (pangoStretches[uidesc->Stretch] == pstretch) break; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 531e1c05..debcb0b2 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -58,6 +58,3 @@ extern void loadFutures(void); extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); - -// drawtext.c -extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); From bffe311afe126d335b3efe6fdbfdc95dde9eb079 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 20:23:18 -0400 Subject: [PATCH 413/487] Switched to using Pango background color attributes. Unix code done for now. --- darwin/attrstr.h | 1 + unix/attrstr.c | 35 ++++++++++------------------------- unix/attrstr.h | 14 +------------- unix/drawtext.c | 9 +-------- unix/future.c | 9 +++++++++ unix/uipriv_unix.h | 1 + 6 files changed, 23 insertions(+), 46 deletions(-) diff --git a/darwin/attrstr.h b/darwin/attrstr.h index f77b4d86..02a3418d 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -77,6 +77,7 @@ extern void uiprivUninitUnderlineColors(void); extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams); // drawtext.m +// TODO figure out where this type should *really* go in all the headers... @interface uiprivDrawTextBackgroundParams : NSObject { size_t start; size_t end; diff --git a/unix/attrstr.c b/unix/attrstr.c index fe554c48..a378e452 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -7,24 +7,8 @@ // TODO make this name less generic? struct foreachParams { PangoAttrList *attrs; - // TODO use pango's built-in background attribute - GPtrArray *backgroundParams; }; -static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) -{ - uiprivDrawTextBackgroundParams *dtb; - - dtb = uiprivNew(uiprivDrawTextBackgroundParams); - dtb->start = start; - dtb->end = end; - dtb->r = r; - dtb->g = g; - dtb->b = b; - dtb->a = a; - g_ptr_array_add(p->backgroundParams, dtb); -} - static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) { if (attr == NULL) // in case of a future attribute @@ -77,8 +61,16 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute (guint16) (a * 65535.0))); break; case uiAttributeTypeBackground: + // TODO make sure this works properly with line paragraph spacings (after figuring out what that means, of course) uiAttributeColor(attr, &r, &g, &b, &a); - addBackgroundAttribute(p, start, end, r, g, b, a); + addattr(p, start, end, + pango_attr_background_new( + (guint16) (r * 65535.0), + (guint16) (g * 65535.0), + (guint16) (b * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_background_alpha_new( + (guint16) (a * 65535.0))); break; case uiAttributeTypeUnderline: switch (uiAttributeUnderline(attr)) { @@ -143,18 +135,11 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute return uiForEachContinue; } -static void freeBackgroundParams(gpointer data) -{ - uiprivFree((uiprivDrawTextBackgroundParams *) data); -} - -PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams) +PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p) { struct foreachParams fep; fep.attrs = pango_attr_list_new(); - fep.backgroundParams = g_ptr_array_new_with_free_func(freeBackgroundParams); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - *backgroundParams = fep.backgroundParams; return fep.attrs; } diff --git a/unix/attrstr.h b/unix/attrstr.h index 5dff27b3..984ac1f5 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -17,16 +17,4 @@ extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const ui extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiFontDescriptor *uidesc); // attrstr.c -extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); - -// drawtext.c -// TODO figure out where this type should *really* go in all the headers... -typedef struct uiprivDrawTextBackgroundParams uiprivDrawTextBackgroundParams; -struct uiprivDrawTextBackgroundParams { - size_t start; - size_t end; - double r; - double g; - double b; - double a; -}; +extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p); diff --git a/unix/drawtext.c b/unix/drawtext.c index b64a9999..5792f9e0 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -5,7 +5,6 @@ struct uiDrawTextLayout { PangoLayout *layout; - GPtrArray *backgroundParams; }; // we need a context for a few things @@ -51,7 +50,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundParams)); + attrs = uiprivAttributedStringToPangoAttrList(p); pango_layout_set_attributes(tl->layout, attrs); pango_attr_list_unref(attrs); @@ -60,18 +59,12 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - g_ptr_array_unref(tl->backgroundParams); g_object_unref(tl->layout); uiprivFree(tl); } void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - guint i; - - for (i = 0; i < tl->backgroundParams->len; i++) { - // TODO - } // TODO have an implicit save/restore on each drawing functions instead? and is this correct? cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); cairo_move_to(c->cr, x, y); diff --git a/unix/future.c b/unix/future.c index 3d63e9e0..68730ead 100644 --- a/unix/future.c +++ b/unix/future.c @@ -8,6 +8,7 @@ // added in pango 1.38; we need 1.36 static PangoAttribute *(*newFeaturesAttr)(const gchar *features) = NULL; static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL; +static PangoAttribute *(*newBGAlphaAttr)(guint16 alpha) = NULL; // added in GTK+ 3.20; we need 3.10 static void (*gwpIterSetObjectName)(GtkWidgetPath *path, gint pos, const char *name) = NULL; @@ -24,6 +25,7 @@ void loadFutures(void) #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) GET(newFeaturesAttr, pango_attr_font_features_new); GET(newFGAlphaAttr, pango_attr_foreground_alpha_new); + GET(newBGAlphaAttr, pango_attr_background_alpha_new); GET(gwpIterSetObjectName, gtk_widget_path_iter_set_object_name); dlclose(handle); } @@ -42,6 +44,13 @@ PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha) return (*newFGAlphaAttr)(alpha); } +PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha) +{ + if (newBGAlphaAttr == NULL) + return NULL; + return (*newBGAlphaAttr)(alpha); +} + gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name) { if (gwpIterSetObjectName == NULL) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index debcb0b2..43ed144b 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -57,4 +57,5 @@ extern GtkCellRenderer *newCellRendererButton(void); extern void loadFutures(void); extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); +extern PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); From e020ba465addecf8f19e490242e3290a34ff6219 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 21:04:38 -0400 Subject: [PATCH 414/487] Moved the old Windows text code out of the way. --- ...old_drawtext.cpp => OLD__old_drawtext.cpp} | 0 windows/{attrstr.cpp => OLD_attrstr.cpp} | 0 windows/{drawtext.cpp => OLD_drawtext.cpp} | 0 windows/{dwrite.cpp => OLD_dwrite.cpp} | 0 .../{fontbutton.cpp => OLD_fontbutton.cpp} | 0 .../{fontdialog.cpp => OLD_fontdialog.cpp} | 0 windows/{graphemes.cpp => OLD_graphemes.cpp} | 0 windows/{opentype.cpp => OLD_opentype.cpp} | 0 windows/OLD_uipriv_attrstr.h | 142 ++++++++++++++++++ windows/_uipriv_migrate.hpp | 26 ---- windows/draw.hpp | 111 -------------- windows/uipriv_windows.hpp | 9 -- 12 files changed, 142 insertions(+), 146 deletions(-) rename windows/{_old_drawtext.cpp => OLD__old_drawtext.cpp} (100%) rename windows/{attrstr.cpp => OLD_attrstr.cpp} (100%) rename windows/{drawtext.cpp => OLD_drawtext.cpp} (100%) rename windows/{dwrite.cpp => OLD_dwrite.cpp} (100%) rename windows/{fontbutton.cpp => OLD_fontbutton.cpp} (100%) rename windows/{fontdialog.cpp => OLD_fontdialog.cpp} (100%) rename windows/{graphemes.cpp => OLD_graphemes.cpp} (100%) rename windows/{opentype.cpp => OLD_opentype.cpp} (100%) create mode 100644 windows/OLD_uipriv_attrstr.h diff --git a/windows/_old_drawtext.cpp b/windows/OLD__old_drawtext.cpp similarity index 100% rename from windows/_old_drawtext.cpp rename to windows/OLD__old_drawtext.cpp diff --git a/windows/attrstr.cpp b/windows/OLD_attrstr.cpp similarity index 100% rename from windows/attrstr.cpp rename to windows/OLD_attrstr.cpp diff --git a/windows/drawtext.cpp b/windows/OLD_drawtext.cpp similarity index 100% rename from windows/drawtext.cpp rename to windows/OLD_drawtext.cpp diff --git a/windows/dwrite.cpp b/windows/OLD_dwrite.cpp similarity index 100% rename from windows/dwrite.cpp rename to windows/OLD_dwrite.cpp diff --git a/windows/fontbutton.cpp b/windows/OLD_fontbutton.cpp similarity index 100% rename from windows/fontbutton.cpp rename to windows/OLD_fontbutton.cpp diff --git a/windows/fontdialog.cpp b/windows/OLD_fontdialog.cpp similarity index 100% rename from windows/fontdialog.cpp rename to windows/OLD_fontdialog.cpp diff --git a/windows/graphemes.cpp b/windows/OLD_graphemes.cpp similarity index 100% rename from windows/graphemes.cpp rename to windows/OLD_graphemes.cpp diff --git a/windows/opentype.cpp b/windows/OLD_opentype.cpp similarity index 100% rename from windows/opentype.cpp rename to windows/OLD_opentype.cpp diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h new file mode 100644 index 00000000..1ef2b71f --- /dev/null +++ b/windows/OLD_uipriv_attrstr.h @@ -0,0 +1,142 @@ +// dwrite.cpp +extern IDWriteFactory *dwfactory; +extern HRESULT initDrawText(void); +extern void uninitDrawText(void); +struct fontCollection { + IDWriteFontCollection *fonts; + WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; + int userLocaleSuccess; +}; +extern fontCollection *loadFontCollection(void); +extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); +extern void fontCollectionFree(fontCollection *fc); +extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); + +// fontdialog.cpp +struct fontDialogParams { + IDWriteFont *font; + double size; + WCHAR *familyName; + WCHAR *styleName; +}; +extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); +extern void loadInitialFontDialogParams(struct fontDialogParams *params); +extern void destroyFontDialogParams(struct fontDialogParams *params); +extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); + +// attrstr.cpp +typedef std::function backgroundFunc; +extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); + +// drawtext.cpp +// TODO reconcile this with attrstr.cpp +class textDrawingEffect : public IUnknown { + ULONG refcount; +public: + bool hasColor; + double r; + double g; + double b; + double a; + + bool hasUnderline; + uiDrawUnderlineStyle u; + + bool hasUnderlineColor; + double ur; + double ug; + double ub; + double ua; + + textDrawingEffect() + { + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + // TODO deduplicate this with common/attrlist.c + bool same(textDrawingEffect *b) + { + static auto boolsDiffer = [](bool a, bool b) -> bool { + if (a && b) + return false; + if (!a && !b) + return false; + return true; + }; + + if (boolsDiffer(this->hasColor, b->hasColor)) + return false; + if (this->hasColor) { + // TODO use a closest match? + if (this->r != b->r) + return false; + if (this->g != b->g) + return false; + if (this->b != b->b) + return false; + if (this->a != b->a) + return false; + } + if (boolsDiffer(this->hasUnderline, b->hasUnderline)) + return false; + if (this->hasUnderline) + if (this->u != b->u) + return false; + if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) + return false; + if (this->hasUnderlineColor) { + // TODO use a closest match? + if (this->ur != b->ur) + return false; + if (this->ug != b->ug) + return false; + if (this->ub != b->ub) + return false; + if (this->ua != b->ua) + return false; + } + return true; + } +}; +// TODO these should not be exported +extern std::map dwriteItalics; +extern std::map dwriteStretches; + +// drawtext.cpp +extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); + +// opentype.cpp +extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); diff --git a/windows/_uipriv_migrate.hpp b/windows/_uipriv_migrate.hpp index b9c365cc..96a67089 100644 --- a/windows/_uipriv_migrate.hpp +++ b/windows/_uipriv_migrate.hpp @@ -12,29 +12,3 @@ extern void uninitDraw(void); extern ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd); extern uiDrawContext *newContext(ID2D1RenderTarget *); extern void freeContext(uiDrawContext *); - -// dwrite.cpp -extern IDWriteFactory *dwfactory; -extern HRESULT initDrawText(void); -extern void uninitDrawText(void); -struct fontCollection { - IDWriteFontCollection *fonts; - WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; - int userLocaleSuccess; -}; -extern fontCollection *loadFontCollection(void); -extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); -extern void fontCollectionFree(fontCollection *fc); -extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); - -// fontdialog.cpp -struct fontDialogParams { - IDWriteFont *font; - double size; - WCHAR *familyName; - WCHAR *styleName; -}; -extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); -extern void loadInitialFontDialogParams(struct fontDialogParams *params); -extern void destroyFontDialogParams(struct fontDialogParams *params); -extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); diff --git a/windows/draw.hpp b/windows/draw.hpp index 8cfbbf72..c271e4db 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -16,114 +16,3 @@ extern ID2D1PathGeometry *pathGeometry(uiDrawPath *p); // drawmatrix.cpp extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); - -// attrstr.cpp -typedef std::function backgroundFunc; -extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); - -// drawtext.cpp -// TODO reconcile this with attrstr.cpp -class textDrawingEffect : public IUnknown { - ULONG refcount; -public: - bool hasColor; - double r; - double g; - double b; - double a; - - bool hasUnderline; - uiDrawUnderlineStyle u; - - bool hasUnderlineColor; - double ur; - double ug; - double ub; - double ua; - - textDrawingEffect() - { - this->refcount = 1; - this->hasColor = false; - this->hasUnderline = false; - this->hasUnderlineColor = false; - } - - // IUnknown - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) - { - if (ppvObject == NULL) - return E_POINTER; - if (riid == IID_IUnknown) { - this->AddRef(); - *ppvObject = this; - return S_OK; - } - *ppvObject = NULL; - return E_NOINTERFACE; - } - - virtual ULONG STDMETHODCALLTYPE AddRef(void) - { - this->refcount++; - return this->refcount; - } - - virtual ULONG STDMETHODCALLTYPE Release(void) - { - this->refcount--; - if (this->refcount == 0) { - delete this; - return 0; - } - return this->refcount; - } - - // TODO deduplicate this with common/attrlist.c - bool same(textDrawingEffect *b) - { - static auto boolsDiffer = [](bool a, bool b) -> bool { - if (a && b) - return false; - if (!a && !b) - return false; - return true; - }; - - if (boolsDiffer(this->hasColor, b->hasColor)) - return false; - if (this->hasColor) { - // TODO use a closest match? - if (this->r != b->r) - return false; - if (this->g != b->g) - return false; - if (this->b != b->b) - return false; - if (this->a != b->a) - return false; - } - if (boolsDiffer(this->hasUnderline, b->hasUnderline)) - return false; - if (this->hasUnderline) - if (this->u != b->u) - return false; - if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) - return false; - if (this->hasUnderlineColor) { - // TODO use a closest match? - if (this->ur != b->ur) - return false; - if (this->ug != b->ug) - return false; - if (this->ub != b->ub) - return false; - if (this->ua != b->ua) - return false; - } - return true; - } -}; -// TODO these should not be exported -extern std::map dwriteItalics; -extern std::map dwriteStretches; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 3f199cbc..3244b7b4 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -152,17 +152,8 @@ extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); // TODO move into a dedicated file abibugs.cpp when we rewrite the drawing code extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); - - - // TODO #include "_uipriv_migrate.hpp" // draw.cpp extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); - -// drawtext.cpp -extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); - -// opentype.cpp -extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); From 6c95ce849ae232b543df826073edab1a47ee3f0b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 21:17:39 -0400 Subject: [PATCH 415/487] Migrated graphemes.cpp and opentype.cpp back. --- windows/OLD_opentype.cpp | 110 ------------------- windows/attrstr.hpp | 7 ++ windows/{OLD_graphemes.cpp => graphemes.cpp} | 5 +- windows/opentype.cpp | 32 ++++++ 4 files changed, 42 insertions(+), 112 deletions(-) delete mode 100644 windows/OLD_opentype.cpp create mode 100644 windows/attrstr.hpp rename windows/{OLD_graphemes.cpp => graphemes.cpp} (93%) create mode 100644 windows/opentype.cpp diff --git a/windows/OLD_opentype.cpp b/windows/OLD_opentype.cpp deleted file mode 100644 index 235f0388..00000000 --- a/windows/OLD_opentype.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// 11 may 2017 -#include "uipriv_windows.hpp" - -typedef std::map tagmap; - -struct uiOpenTypeFeatures { - tagmap *tags; -}; - -uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) -{ - uiOpenTypeFeatures *otf; - - otf = uiNew(uiOpenTypeFeatures); - otf->tags = new tagmap; - return otf; -} - -void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) -{ - delete otf->tags; - uiFree(otf); -} - -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) -{ - uiOpenTypeFeatures *out; - - out = uiNew(uiOpenTypeFeatures); - out->tags = new tagmap; - *(out->tags) = *(otf->tags); - return out; -} - -#define mktag(a, b, c, d) ((uint32_t) DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d)) - -void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) -{ - (*(otf->tags))[mktag(a, b, c, d)] = value; -} - -void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) -{ - // this will just return 0 if nothing was removed (if I'm reading the help pages I've found correctly) - otf->tags->erase(mktag(a, b, c, d)); -} - -// TODO will the const wreck stuff up? -int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) -{ - tagmap::const_iterator iter; - - iter = otf->tags->find(mktag(a, b, c, d)); - if (iter == otf->tags->end()) - return 0; - *value = iter->second; - return 1; -} - -void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) -{ - tagmap::const_iterator iter, end; - - end = otf->tags->end(); - for (iter = otf->tags->begin(); iter != end; iter++) { - uint8_t a, b, c, d; - uiForEach ret; - - a = (uint8_t) (iter->first & 0xFF); - b = (uint8_t) ((iter->first >> 8) & 0xFF); - c = (uint8_t) ((iter->first >> 16) & 0xFF); - d = (uint8_t) ((iter->first >> 24) & 0xFF); - ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, - iter->second, data); - if (ret == uiForEachStop) - return; - } -} - -int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) -{ - if (a == NULL && b == NULL) - return 1; - if (a == NULL || b == NULL) - return 0; - // TODO make sure this is correct - return *(a->tags) == *(b->tags); -} - -IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf) -{ - IDWriteTypography *dt; - tagmap::const_iterator iter, end; - DWRITE_FONT_FEATURE dff; - HRESULT hr; - - hr = dwfactory->CreateTypography(&dt); - if (hr != S_OK) - logHRESULT(L"error creating IDWriteTypography", hr); - end = otf->tags->end(); - for (iter = otf->tags->begin(); iter != end; iter++) { - ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); - dff.nameTag = (DWRITE_FONT_FEATURE_TAG) (iter->first); - dff.parameter = (UINT32) (iter->second); - hr = dt->AddFontFeature(dff); - if (hr != S_OK) - logHRESULT(L"error adding OpenType feature to IDWriteTypography", hr); - } - return dt; -} diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp new file mode 100644 index 00000000..0b1d9031 --- /dev/null +++ b/windows/attrstr.hpp @@ -0,0 +1,7 @@ +// 11 march 2018 +extern "C" { +#include "../common/attrstr.h" +} + +// opentype.cpp +extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf); diff --git a/windows/OLD_graphemes.cpp b/windows/graphemes.cpp similarity index 93% rename from windows/OLD_graphemes.cpp rename to windows/graphemes.cpp index 256c3a07..63e4882f 100644 --- a/windows/OLD_graphemes.cpp +++ b/windows/graphemes.cpp @@ -1,16 +1,17 @@ // 25 may 2016 #include "uipriv_windows.hpp" +#include "attrstr.hpp" // We could use CharNextW() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). // We could also use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html, http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/), but its rules for buffer sizes is convoluted. // Let's just deal with the CharNextW() bug. -int graphemesTakesUTF16(void) +int uiprivGraphemesTakesUTF16(void) { return 1; } -struct graphemes *graphemes(void *s, size_t len) +struct graphemes *uiprivNewGraphemes(void *s, size_t len) { struct graphemes *g; WCHAR *str; diff --git a/windows/opentype.cpp b/windows/opentype.cpp new file mode 100644 index 00000000..0a96cdc4 --- /dev/null +++ b/windows/opentype.cpp @@ -0,0 +1,32 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" +#include "attrstr.hpp" + +// TODO pull out my decision for empty uiOpenTypeFeatures, assuming that it isn't in another file or that I even made one + +static uiForEach addToTypography(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + IDWriteTypography *dt = (IDWriteTypography *) data; + DWRITE_FONT_FEATURE dff; + HRESULT hr; + + ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); + dff.nameTag = /*(DWRITE_FONT_FEATURE_TAG)*/ DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d); + dff.parameter = (UINT32) value; + hr = dt->AddFontFeature(dff); + if (hr != S_OK) + logHRESULT(L"error adding OpenType feature to IDWriteTypography", hr); + return uiForEachContinue; +} + +IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf) +{ + IDWriteTypography *dt; + HRESULT hr; + + hr = dwfactory->CreateTypography(&dt); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTypography", hr); + uiOpenTypeFeaturesForEach(otf, addToTypography, dt); + return dt; +} From 1f61fb30de59f362f65af3f9545c9861a168358d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 22:11:19 -0400 Subject: [PATCH 416/487] Wrote a fontmatch.cpp. --- windows/OLD_drawtext.cpp | 31 +++-------------------------- windows/attrstr.hpp | 5 +++++ windows/fontmatch.cpp | 43 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 windows/fontmatch.cpp diff --git a/windows/OLD_drawtext.cpp b/windows/OLD_drawtext.cpp index 84a8874e..0db8fc9b 100644 --- a/windows/OLD_drawtext.cpp +++ b/windows/OLD_drawtext.cpp @@ -30,26 +30,6 @@ struct uiDrawTextLayout { // fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) -// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -std::map dwriteItalics = { - { uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, - { uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, - { uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, -}; - -// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -std::map dwriteStretches = { - { uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, - { uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, - { uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, - { uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, - { uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, - { uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, - { uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, - { uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, - { uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, -}; - struct lineInfo { size_t startPos; // in UTF-16 points size_t endPos; @@ -154,14 +134,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) wDefaultFamily = toUTF16(p->DefaultFont->Family); hr = dwfactory->CreateTextFormat( wDefaultFamily, NULL, - // for the most part, DirectWrite weights correlate to ours - // the differences: - // - Minimum — libui: 0, DirectWrite: 1 - // - Maximum — libui: 1000, DirectWrite: 999 - // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) - (DWRITE_FONT_WEIGHT) (p->DefaultFont->Weight), - dwriteItalics[p->DefaultFont->Italic], - dwriteStretches[p->DefaultFont->Stretch], + uiprivWeightToDWriteWeight(p->DefaultFont->Weight), + uiprivItalicToDWriteStyle(p->DefaultFont->Italic), + uiprivStretchToDWriteStretch(p->DefaultFont->Stretch), pointSizeToDWriteSize(p->DefaultFont->Size), // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx // TODO use the current locale? diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 0b1d9031..ca1226c5 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -5,3 +5,8 @@ extern "C" { // opentype.cpp extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf); + +// fontmatch.cpp +extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); +extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); +extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp new file mode 100644 index 00000000..05986dc1 --- /dev/null +++ b/windows/fontmatch.cpp @@ -0,0 +1,43 @@ +// 11 march 2018 +#include "uipriv_windows.hpp" +#include "attrstr.hpp" + +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteItalics = { + { uiTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, + { uiTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, + { uiTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, +}; + +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteStretches = { + { uiTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, + { uiTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, + { uiTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, + { uiTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, + { uiTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, + { uiTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, + { uiTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, + { uiTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, + { uiTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, +}; + +// for the most part, DirectWrite weights correlate to ours +// the differences: +// - Minimum — libui: 0, DirectWrite: 1 +// - Maximum — libui: 1000, DirectWrite: 999 +// TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) +DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w) +{ + return (DWRITE_FONT_WEIGHT) w; +} + +DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i) +{ + return dwriteItalics[i]; +} + +DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s) +{ + return dwriteStretches[s]; +} From 86264d32a01f7ae3538646e6845c9f1c05e90732 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 22:17:16 -0400 Subject: [PATCH 417/487] And migrated the IDWriteFont -> uiFontDescriptor code. --- windows/OLD_drawtext.cpp | 19 ------------------- windows/OLD_uipriv_attrstr.h | 9 --------- windows/attrstr.hpp | 1 + windows/fontmatch.cpp | 18 ++++++++++++++++++ 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/windows/OLD_drawtext.cpp b/windows/OLD_drawtext.cpp index 0db8fc9b..201d3a1d 100644 --- a/windows/OLD_drawtext.cpp +++ b/windows/OLD_drawtext.cpp @@ -665,22 +665,3 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) // and there doesn't seem to be this either... (TODO check what PadWrite does?) p->xoff = 0; } - -// TODO split this and the above related font matching code into a separate file? -void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) -{ - DWRITE_FONT_STYLE dwitalic; - DWRITE_FONT_STRETCH dwstretch; - - dwitalic = font->GetStyle(); - // TODO reverse the above misalignment if it is corrected - uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); - dwstretch = font->GetStretch(); - - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) - if (dwriteItalics[uidesc->Italic] == dwitalic) - break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) - if (dwriteStretches[uidesc->Stretch] == dwstretch) - break; -} diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index 1ef2b71f..360ec9e2 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -131,12 +131,3 @@ public: return true; } }; -// TODO these should not be exported -extern std::map dwriteItalics; -extern std::map dwriteStretches; - -// drawtext.cpp -extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); - -// opentype.cpp -extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index ca1226c5..56523aa5 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -10,3 +10,4 @@ extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpen extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); +extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp index 05986dc1..034b9907 100644 --- a/windows/fontmatch.cpp +++ b/windows/fontmatch.cpp @@ -41,3 +41,21 @@ DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s) { return dwriteStretches[s]; } + +void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) +{ + DWRITE_FONT_STYLE dwitalic; + DWRITE_FONT_STRETCH dwstretch; + + dwitalic = font->GetStyle(); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); + dwstretch = font->GetStretch(); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (dwriteItalics[uidesc->Italic] == dwitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (dwriteStretches[uidesc->Stretch] == dwstretch) + break; +} From abc6fd282513ca4b4577736ce3d341a55e0dbaae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 22:36:31 -0400 Subject: [PATCH 418/487] uiDrawFontDescriptor -> uiFontDescriptor. --- windows/attrstr.hpp | 2 +- windows/fontmatch.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 56523aa5..074a8830 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -10,4 +10,4 @@ extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpen extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); -extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc); diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp index 034b9907..28b4c84a 100644 --- a/windows/fontmatch.cpp +++ b/windows/fontmatch.cpp @@ -42,7 +42,7 @@ DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s) return dwriteStretches[s]; } -void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) +void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc) { DWRITE_FONT_STYLE dwitalic; DWRITE_FONT_STRETCH dwstretch; From 12e97a1b29a6c2caf1e5b247844b8f393746b1ad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 18:43:32 -0400 Subject: [PATCH 419/487] Started migrating attrstr.cpp, using the same techniques as attrstr.m. --- darwin/attrstr.m | 2 +- windows/{OLD_attrstr.cpp => attrstr.cpp} | 235 ++++++++++++++++------- 2 files changed, 170 insertions(+), 67 deletions(-) rename windows/{OLD_attrstr.cpp => attrstr.cpp} (52%) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 180bf272..ca32216c 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -257,7 +257,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ else cfa = [cfa copy]; [cfa addAttribute:attr]; - // clamp effectiveRange within [start, end) + // clamp range within [start, end) if (range.location < start) { diff = start - range.location; range.location = start; diff --git a/windows/OLD_attrstr.cpp b/windows/attrstr.cpp similarity index 52% rename from windows/OLD_attrstr.cpp rename to windows/attrstr.cpp index c87804b5..92c39b9a 100644 --- a/windows/OLD_attrstr.cpp +++ b/windows/attrstr.cpp @@ -1,38 +1,147 @@ // 12 february 2017 #include "uipriv_windows.hpp" -#include "draw.hpp" +#include "attrstr.hpp" // TODO this whole file needs cleanup -// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() -// we also need to collect all the background blocks and add them all at once +// we need to collect all the background blocks and add them all at once // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; size_t len; IDWriteTextLayout *layout; - std::map *effects; std::vector *backgroundFuncs; }; -static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function f) -{ - size_t i; - size_t *key; - textDrawingEffect *t; +// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() +// we also want to combine identical effects, which DirectWrite doesn't seem to provide a way to do +// we can at least try to goad it into doing so if we can deduplicate effects once they're all computed +// so what we do is use this class to store in-progress effects, much like uiprivCombinedFontAttr on the OS X code +// we then deduplicate them later while converting them into a form suitable for drawing with; see applyEffectsAttributes() below +class combinedEffectsAttr : public IUnknown { + ULONG refcount; + uiAttribute *colorAttr; + uiAttribute *underlineAttr; + uiAttribute *underlineColorAttr; - // TODO explain why we make one for every character - for (i = start; i < end; i++) { - t = (*(p->effects))[i]; - if (t != NULL) { - f(t); - continue; + void setAttribute(uiAttribute *a) + { + if (a == NULL) + return; + switch (uiAttributeGetType(a)) { + case uiAttributeTypeColor: + if (this->colorAttr != NULL) + uiprivAttributeRelease(this->colorAttr); + this->colorAttr = uiprivAttributeRetain(a); + break; + case uiAttributeTypeUnderline: + if (this->underlineAttr != NULL) + uiprivAttributeRelease(this->underlineAttr); + this->underlineAttr = uiprivAttributeRetain(a); + break; + case uiAttributeTypeUnderlineColor: + if (this->underlineAttr != NULL) + uiprivAttributeRelease(this->underlineAttr); + this->underlineColorAttr = uiprivAttributeRetain(a); + break; } - t = new textDrawingEffect; - f(t); - (*(p->effects))[i] = t; } +public: + combinedEffectsAttr(uiAttribute *a) + { + this->refcount = 1; + this->colorAttr = NULL; + this->underlineAttr = NULL; + this->underlineColorAttr = NULL; + this->setAttribute(a); + } + + ~combinedEffectsAttr() + { + if (this->colorAttr != NULL) + uiprivAttributeRelease(this->colorAttr); + if (this->underlineAttr != NULL) + uiprivAttributeRelease(this->underlineAttr); + if (this->underlineColorAttr != NULL) + uiprivAttributeRelease(this->underlineColorAttr); + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + combinedEffectsAttr *cloneWith(uiAttribute *a) + { + combinedEffectsAttr *b; + + b = new combinedEffectsAttr(this->colorAttr); + b->setAttribute(this->underlineAttr); + b->setAttribute(this->underlineColorAttr); + b->setAttribute(a); + return b; + } +}; + +static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, size_t end, uiAttribute *attr) +{ + IUnknown *u; + combinedEffectsAttr *cea; + DWRITE_TEXT_RANGE range; + size_t diff; + HRESULT hr; + + while (start < end) { + hr = p->layout->GetDrawingEffect(start, &u, &range); + if (hr != S_OK) +{logHRESULT(L"HELP", hr); + return hr; +} cea = (combinedEffectsAttr *) u; + if (cea == NULL) + cea = new combinedEffectsAttr(attr); + else + cea = cea->cloneWith(attr); + // clamp range within [start, end) + if (range.startPosition < start) { + diff = start - range.startPosition; + range.startPosition = start; + range.length -= diff; + } + if ((range.startPosition + range.length) > end) + range.length = end - range.startPosition; + hr = p->layout->SetDrawingEffect(cea, range); + if (hr != S_OK) + return hr; + // TODO figure out what and when needs to be released + start += range.length; + } + return S_OK; } static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) @@ -49,7 +158,7 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; @@ -66,76 +175,69 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); range.startPosition = start; range.length = end - start; - switch (spec->Type) { - case uiAttributeFamily: - wfamily = toUTF16(spec->Family); + switch (uiAttributeGetType(attr)) { + case uiAttributeTypeFamily: + wfamily = toUTF16(uiAttributeFamily(attr)); hr = p->layout->SetFontFamilyName(wfamily, range); if (hr != S_OK) logHRESULT(L"error applying family name attribute", hr); uiFree(wfamily); break; - case uiAttributeSize: + case uiAttributeTypeSize: hr = p->layout->SetFontSize( -// TODO unify with drawtext.cpp +// TODO unify with fontmatch.cpp and/or attrstr.hpp #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) - pointSizeToDWriteSize(spec->Double), + pointSizeToDWriteSize(uiAttributeSize(attr)), range); if (hr != S_OK) logHRESULT(L"error applying size attribute", hr); break; - case uiAttributeWeight: - // TODO reverse the misalignment from drawtext.cpp if it is corrected + case uiAttributeTypeWeight: hr = p->layout->SetFontWeight( - (DWRITE_FONT_WEIGHT) (spec->Value), + uiprivWeightToDWriteWeight(uiAttributeWeight(attr)), range); if (hr != S_OK) logHRESULT(L"error applying weight attribute", hr); break; - case uiAttributeItalic: + case uiAttributeTypeItalic: hr = p->layout->SetFontStyle( - dwriteItalics[(uiDrawTextItalic) (spec->Value)], + uiprivItalicToDWriteStyle(uiAttributeItalic(attr)), range); if (hr != S_OK) logHRESULT(L"error applying italic attribute", hr); break; - case uiAttributeStretch: + case uiAttributeTypeStretch: hr = p->layout->SetFontStretch( - dwriteStretches[(uiDrawTextStretch) (spec->Value)], + uiprivStretchToDWriteStretch(uiAttributeStretch(attr)), range); if (hr != S_OK) logHRESULT(L"error applying stretch attribute", hr); break; - case uiAttributeColor: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { - t->hasColor = true; - t->r = spec->R; - t->g = spec->G; - t->b = spec->B; - t->a = spec->A; - }); + case uiAttributeTypeUnderline: + // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method + hasUnderline = FALSE; + if (uiAttributeUnderline(attr) != uiUnderlineNone) + hasUnderline = TRUE; + hr = p->layout->SetUnderline(hasUnderline, range); + if (hr != S_OK) + logHRESULT(L"error applying underline attribute", hr); + // and fall through to set the underline style through the drawing effect + case uiAttributeTypeColor: + case uiAttributeTypeUnderlineColor: + hr = addEffectAttributeToRange(p, start, end, attr); + if (hr != S_OK) + logHRESULT(L"error applying effect (color, underline, or underline color) attribute", hr); break; case uiAttributeBackground: p->backgroundFuncs->push_back( mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; - case uiAttributeUnderline: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderline = true; - t->u = (uiDrawUnderlineStyle) (spec->Value); - }); - // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method - hasUnderline = FALSE; - if ((uiDrawUnderlineStyle) (spec->Value) != uiDrawUnderlineStyleNone) - hasUnderline = TRUE; - hr = p->layout->SetUnderline(hasUnderline, range); - if (hr != S_OK) - logHRESULT(L"error applying underline attribute", hr); - break; - case uiAttributeUnderlineColor: +#if 0 +TODO switch (spec->Value) { case uiDrawUnderlineColorCustom: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = spec->R; t->ug = spec->G; @@ -146,7 +248,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute // TODO see if Microsoft has any standard colors for this case uiDrawUnderlineColorSpelling: // TODO GtkTextView style property error-underline-color - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 1.0; t->ug = 0.0; @@ -155,7 +257,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute }); break; case uiDrawUnderlineColorGrammar: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 0.0; t->ug = 1.0; @@ -164,7 +266,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute }); break; case uiDrawUnderlineColorAuxiliary: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 0.0; t->ug = 0.0; @@ -174,24 +276,25 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; } break; - case uiAttributeFeatures: - // only generate an attribute if spec->Features is not NULL - if (spec->Features == NULL) +#endif + case uiAttributeTypeFeatures: + // only generate an attribute if not NULL + // TODO do we still need to do this or not... + if (uiAttributeFeatures(attr) == NULL) break; - dt = otfToDirectWrite(spec->Features); + dt = uiprivOpenTypeFeaturesToIDWriteTypography(uiAttributeFeatures(attr)); hr = p->layout->SetTypography(dt, range); if (hr != S_OK) logHRESULT(L"error applying features attribute", hr); dt->Release(); break; - default: - // TODO complain - ; } return uiForEachContinue; } -static void applyAndFreeEffectsAttributes(struct foreachParams *p) +$$$$TODO CONTINUE HERE + +static void applyEffectsAttributes(struct foreachParams *p) { size_t i, n; textDrawingEffect *effect, *effectb; From f25b8dce37dec5bcbb6ba62f77f6f0d54b5fd449 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 22:01:15 -0400 Subject: [PATCH 420/487] Finished migrating attrstr.cpp. --- darwin/attrstr.m | 2 +- windows/OLD_uipriv_attrstr.h | 79 +----------- windows/attrstr.cpp | 243 ++++++++++++++++++++++------------- windows/attrstr.hpp | 35 +++++ windows/winapi.hpp | 1 + 5 files changed, 199 insertions(+), 161 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index ca32216c..7d62bcfe 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -410,7 +410,7 @@ static void applyFontAttributes(CFMutableAttributedStringRef mas, uiFontDescript CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); CFRelease(font); - // now go through, replacing every consecutive uiprivCombinedFontAttr with the proper CTFontRef + // now go through, replacing every uiprivCombinedFontAttr with the proper CTFontRef // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself range.location = 0; while (range.location < n) { diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index 360ec9e2..b50a7fe2 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -29,34 +29,14 @@ typedef std::function **backgroundFuncs); // drawtext.cpp -// TODO reconcile this with attrstr.cpp -class textDrawingEffect : public IUnknown { - ULONG refcount; -public: - bool hasColor; - double r; - double g; - double b; - double a; +textDrawingEffect:textDrawingEffect(void) +{ + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; +} - bool hasUnderline; - uiDrawUnderlineStyle u; - - bool hasUnderlineColor; - double ur; - double ug; - double ub; - double ua; - - textDrawingEffect() - { - this->refcount = 1; - this->hasColor = false; - this->hasUnderline = false; - this->hasUnderlineColor = false; - } - - // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { if (ppvObject == NULL) @@ -85,49 +65,4 @@ public: } return this->refcount; } - - // TODO deduplicate this with common/attrlist.c - bool same(textDrawingEffect *b) - { - static auto boolsDiffer = [](bool a, bool b) -> bool { - if (a && b) - return false; - if (!a && !b) - return false; - return true; - }; - - if (boolsDiffer(this->hasColor, b->hasColor)) - return false; - if (this->hasColor) { - // TODO use a closest match? - if (this->r != b->r) - return false; - if (this->g != b->g) - return false; - if (this->b != b->b) - return false; - if (this->a != b->a) - return false; - } - if (boolsDiffer(this->hasUnderline, b->hasUnderline)) - return false; - if (this->hasUnderline) - if (this->u != b->u) - return false; - if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) - return false; - if (this->hasUnderlineColor) { - // TODO use a closest match? - if (this->ur != b->ur) - return false; - if (this->ug != b->ug) - return false; - if (this->ub != b->ub) - return false; - if (this->ua != b->ua) - return false; - } - return true; - } }; diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 92c39b9a..ad16a70b 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -14,6 +14,8 @@ struct foreachParams { std::vector *backgroundFuncs; }; +static std::hash doubleHash; + // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() // we also want to combine identical effects, which DirectWrite doesn't seem to provide a way to do // we can at least try to goad it into doing so if we can deduplicate effects once they're all computed @@ -47,6 +49,17 @@ class combinedEffectsAttr : public IUnknown { break; } } + + // this is needed by applyEffectsAttributes() below + // TODO doesn't uiprivAttributeEqual() already do this; if it doesn't, make it so; if (or when) it does, fix all platforms to avoid this extra check + static bool attrEqual(uiAttribute *a, uiAttribute *b) const + { + if (a == NULL && b == NULL) + return true; + if (a == NULL || b == NULL) + return false; + return uiprivAttributeEqual(a, b); + } public: combinedEffectsAttr(uiAttribute *a) { @@ -107,6 +120,102 @@ public: b->setAttribute(a); return b; } + + // and these are also needed by applyEffectsAttributes() below + size_t hash(void) const noexcept + { + size_t ret = 0; + double r, g, b, a; + + if (self->colorAttr != NULL) { + uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + ret ^= doubleHash(r); + ret ^= doubleHash(g); + ret ^= doubleHash(b); + ret ^= doubleHash(a); + } + if (self->underlineAttr != NULL) + ret ^= (size_t) uiAttributeUnderline(self->underlineAttr); + if (self->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(self->underlineColorAttr, &colorType, &r, &g, &b, &a); + ret ^= (size_t) colorType; + ret ^= doubleHash(r); + ret ^= doubleHash(g); + ret ^= doubleHash(b); + ret ^= doubleHash(a); + } + return ret; + } + + bool equals(const combinedEffectsAttr *b) const + { + if (b == NULL) + return false; + return combinedEffectsAttr::attrEqual(a->colorAttr, b->colorAttr) && + combinedEffectsAttr::attrEqual(a->underilneAttr, b->underlineAttr) && + combinedEffectsAttr::attrEqual(a->underlineColorAttr, b->underlineColorAttr); + } + + drawingEffectsAttr *toDrawingEffectsAttr(void) + { + drawingEffectsAttr *dea; + double r, g, b, a; + uiUnderlineColor colorType; + + dea = new drawingEffectsAttr; + if (self->colorAttr != NULL) { + uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + dea->addColor(r, g, b, a); + } + if (self->underlineAttr != NULL) + dea->addUnderline(uiAttributeUnderline(self->underlineAttr)); + if (self->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(self->underlineColor, &colorType, &r, &g, &b, &a); + // TODO see if Microsoft has any standard colors for these + switch (colorType) { + case uiUnderlineColorSpelling: + // TODO consider using the GtkTextView style property error-underline-color here if Microsoft has no preference + r = 1.0; + g = 0.0; + b = 0.0; + a = 1.0; + break; + case uiUnderlineColorGrammar: + r = 0.0; + g = 1.0; + b = 0.0; + a = 1.0; + break; + case uiUnderlineColorAlternate: + r = 0.0; + g = 0.0; + b = 1.0; + a = 1.0; + break; + } + dea->addUnderlineColor(r, g, b, a); + } + return dea; + } +}; + +// also needed by applyEffectsAttributes() below +class applyEffectsHash { +public: + typedef combinedEffectsAttr *ceaptr; + size_t operator()(applyEffectsHash::ceaptr const &cea) const noexcept + { + return cea->hash(); + } +}; + +class applyEffectsEqualTo { +public: + typedef combinedEffectsAttr *ceaptr; + bool operator()(const applyEffectsEqualTo::ceaptr &a, const applyEffectsEqualTo::ceaptr &b) const + { + return a->equals(b); + } }; static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, size_t end, uiAttribute *attr) @@ -121,6 +230,7 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, hr = p->layout->GetDrawingEffect(start, &u, &range); if (hr != S_OK) {logHRESULT(L"HELP", hr); + // TODO proper cleanup somehow return hr; } cea = (combinedEffectsAttr *) u; if (cea == NULL) @@ -137,6 +247,7 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, range.length = end - range.startPosition; hr = p->layout->SetDrawingEffect(cea, range); if (hr != S_OK) + // TODO proper cleanup somehow return hr; // TODO figure out what and when needs to be released start += range.length; @@ -233,50 +344,6 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; -#if 0 -TODO - switch (spec->Value) { - case uiDrawUnderlineColorCustom: - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = spec->R; - t->ug = spec->G; - t->ub = spec->B; - t->ua = spec->A; - }); - break; - // TODO see if Microsoft has any standard colors for this - case uiDrawUnderlineColorSpelling: - // TODO GtkTextView style property error-underline-color - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = 1.0; - t->ug = 0.0; - t->ub = 0.0; - t->ua = 1.0; - }); - break; - case uiDrawUnderlineColorGrammar: - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = 0.0; - t->ug = 1.0; - t->ub = 0.0; - t->ua = 1.0; - }); - break; - case uiDrawUnderlineColorAuxiliary: - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = 0.0; - t->ug = 0.0; - t->ub = 1.0; - t->ua = 1.0; - }); - break; - } - break; -#endif case uiAttributeTypeFeatures: // only generate an attribute if not NULL // TODO do we still need to do this or not... @@ -292,66 +359,66 @@ TODO return uiForEachContinue; } -$$$$TODO CONTINUE HERE - -static void applyEffectsAttributes(struct foreachParams *p) +static HRESULT applyEffectsAttributes(struct foreachParams *p) { - size_t i, n; - textDrawingEffect *effect, *effectb; + IUnknown *u; + combinedEffectsAttr *cea; + drawingEffectsAttr *dea; DWRITE_TEXT_RANGE range; - static auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { - HRESULT hr; + // here's the magic: this std::unordered_map will deduplicate all of our combinedEffectsAttrs, mapping all identical ones to a single drawingEffectsAttr + // because drawingEffectsAttr is the *actual* drawing effect we want for rendering, we also replace the combinedEffectsAttrs with them in the IDWriteTextLayout at the same time + // note the use of our custom hash and equal_to implementations + std::unordered_map effects; + HRESULT hr; - if (effect == NULL) - return; - hr = layout->SetDrawingEffect(effect, range); - if (hr != S_OK) - logHRESULT(L"error applying drawing effects attributes", hr); - effect->Release(); - }; - - // go through, fililng in the effect attribute for successive ranges of identical textDrawingEffects - // we are best off treating series of identical effects as single ranges ourselves for parity across platforms, even if Windows does something similar itself - // this also avoids breaking apart surrogate pairs (though IIRC Windows doing the something similar itself might make this a non-issue here) - effect = NULL; - n = p->len; + // go through, replacing every combinedEffectsAttr with the proper drawingEffectsAttr range.startPosition = 0; - for (i = 0; i < n; i++) { - effectb = (*(p->effects))[i]; - // run of no effect? - if (effect == NULL && effectb == NULL) - continue; - // run of the same effect? - if (effect != NULL && effectb != NULL) - if (effect->same(effectb)) { - effectb->Release(); - continue; + while (range.startPosition < p->len) { + hr = p->layout->GetDrawingEffect(range.startPosition, &u, &range); + if (hr != S_OK) + // TODO proper cleanup somehow + return hr; + cea = (combinedEffectsAttr *) cea; + if (cea != NULL) { + auto diter = effects.find(cea); + if (diter != effects.end()) + dea = diter->second; + else { + dea = cea->toDrawingEffectsAttr(); + effects.insert(cea, dea); } - - // the effect has changed; commit the old effect - range.length = i - range.startPosition; - apply(p->layout, effect, range); - - range.startPosition = i; - effect = effectb; + hr = p->layout->SetDrawingEffect(dea, range); + if (hr != S_OK) + // TODO proper cleanup somehow + return hr; + } + range.startPosition += range.length; } - // and apply the last effect - range.length = i - range.startPosition; - apply(p->layout, effect, range); - delete p->effects; + // and clean up, finally destroying the combinedEffectAttrs too +#if 0 +TODO + for (auto iter = effects.begin(); iter != effects.end(); iter++) { + iter->first->Release(); + iter->second->Release(); + } +#endif + return S_OK; } -void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) +void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) { struct foreachParams fep; + HRESULT hr; fep.s = attrstrUTF16(p->String); fep.len = attrstrUTF16Len(p->String); fep.layout = layout; - fep.effects = new std::map; fep.backgroundFuncs = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - applyAndFreeEffectsAttributes(&fep); + hr = applyEffectsAttributes(&fep); + if (hr != S_OK) + logHRESULT(L"error applying effects attributes", hr); *backgroundFuncs = fep.backgroundFuncs; } diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 074a8830..3137d75a 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -11,3 +11,38 @@ extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc); + +// attrstr.cpp +extern void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); + +// drawtext.cpp +class drawingEffectsAttr : public IUnknown { + ULONG refcount; + + bool hasColor; + double r; + double g; + double b; + double a; + + bool hasUnderline; + uiUnderline u; + + bool hasUnderlineColor; + double ur; + double ug; + double ub; + double ua; +public: + textDrawingEffect(); + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(void); + virtual ULONG STDMETHODCALLTYPE Release(void); + + void setColor(double r, double g, double b, double a); + void setUnderline(uiUnderline u); + void setUnderlineColor(double r, double g, double b, double a); + HRESULT draw(TODO); +}; diff --git a/windows/winapi.hpp b/windows/winapi.hpp index c5796faf..1b2ab000 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -46,6 +46,7 @@ #include #include +#include #include #include #endif From 528295168122ecade75465b17ba9d462de80b1f5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 22:06:33 -0400 Subject: [PATCH 421/487] Oops, self -> this. --- windows/attrstr.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index ad16a70b..a1455b71 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -127,17 +127,17 @@ public: size_t ret = 0; double r, g, b, a; - if (self->colorAttr != NULL) { - uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + if (this->colorAttr != NULL) { + uiAttributeColor(this->colorAttr, &r, &g, &b, &a); ret ^= doubleHash(r); ret ^= doubleHash(g); ret ^= doubleHash(b); ret ^= doubleHash(a); } - if (self->underlineAttr != NULL) - ret ^= (size_t) uiAttributeUnderline(self->underlineAttr); - if (self->underlineColorAttr != NULL) { - uiAttributeUnderlineColor(self->underlineColorAttr, &colorType, &r, &g, &b, &a); + if (this->underlineAttr != NULL) + ret ^= (size_t) uiAttributeUnderline(this->underlineAttr); + if (this->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(this->underlineColorAttr, &colorType, &r, &g, &b, &a); ret ^= (size_t) colorType; ret ^= doubleHash(r); ret ^= doubleHash(g); @@ -163,14 +163,14 @@ public: uiUnderlineColor colorType; dea = new drawingEffectsAttr; - if (self->colorAttr != NULL) { - uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + if (this->colorAttr != NULL) { + uiAttributeColor(this->colorAttr, &r, &g, &b, &a); dea->addColor(r, g, b, a); } - if (self->underlineAttr != NULL) - dea->addUnderline(uiAttributeUnderline(self->underlineAttr)); - if (self->underlineColorAttr != NULL) { - uiAttributeUnderlineColor(self->underlineColor, &colorType, &r, &g, &b, &a); + if (this->underlineAttr != NULL) + dea->addUnderline(uiAttributeUnderline(this->underlineAttr)); + if (this->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(this->underlineColor, &colorType, &r, &g, &b, &a); // TODO see if Microsoft has any standard colors for these switch (colorType) { case uiUnderlineColorSpelling: From 5314295e4cfe88838671273537a588daab52f438 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 22:56:30 -0400 Subject: [PATCH 422/487] Started migrating drawtext.cpp. This is a bigger mess than I was hoping for... --- windows/OLD_uipriv_attrstr.h | 39 -- windows/attrstr.hpp | 6 +- windows/drawtext.cpp | 669 +++++++++++++++++++++++++++++++++++ 3 files changed, 673 insertions(+), 41 deletions(-) create mode 100644 windows/drawtext.cpp diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index b50a7fe2..a183b3b0 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -27,42 +27,3 @@ extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); // attrstr.cpp typedef std::function backgroundFunc; extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); - -// drawtext.cpp -textDrawingEffect:textDrawingEffect(void) -{ - this->refcount = 1; - this->hasColor = false; - this->hasUnderline = false; - this->hasUnderlineColor = false; -} - - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) - { - if (ppvObject == NULL) - return E_POINTER; - if (riid == IID_IUnknown) { - this->AddRef(); - *ppvObject = this; - return S_OK; - } - *ppvObject = NULL; - return E_NOINTERFACE; - } - - virtual ULONG STDMETHODCALLTYPE AddRef(void) - { - this->refcount++; - return this->refcount; - } - - virtual ULONG STDMETHODCALLTYPE Release(void) - { - this->refcount--; - if (this->refcount == 0) { - delete this; - return 0; - } - return this->refcount; - } -}; diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 3137d75a..6c0bce7f 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -34,7 +34,7 @@ class drawingEffectsAttr : public IUnknown { double ub; double ua; public: - textDrawingEffect(); + drawingEffectsAttr(void); // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); @@ -44,5 +44,7 @@ public: void setColor(double r, double g, double b, double a); void setUnderline(uiUnderline u); void setUnderlineColor(double r, double g, double b, double a); - HRESULT draw(TODO); + HRESULT mkColorBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b); + HRESULT underline(uiUnderline *u); + HRESULT mkUnderlineBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b); }; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp new file mode 100644 index 00000000..c9baef49 --- /dev/null +++ b/windows/drawtext.cpp @@ -0,0 +1,669 @@ +// 17 january 2017 +#include "uipriv_windows.hpp" +#include "draw.hpp" +#include "attrstr.hpp" + +// TODO verify our renderer is correct, especially with regards to snapping + +struct uiDrawTextLayout { + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + std::vector *backgroundFuncs; + // for converting DirectWrite indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; + size_t *u16tou8; + size_t nUTF16; +}; + +// TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here + +// typographic points are 1/72 inch; this parameter is 1/96 inch +// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx +#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) + +// TODO move this and the layout creation stuff to attrstr.cpp like the other ports, or move the other ports into their drawtext.* files +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteAligns = { + { uiDrawTextAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, + { uiDrawTextAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, + { uiDrawTextAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) +{ + uiDrawTextLayout *tl; + WCHAR *wDefaultFamily; + DWRITE_WORD_WRAPPING wrap; + FLOAT maxWidth; + HRESULT hr; + + tl = uiNew(uiDrawTextLayout); + + wDefaultFamily = toUTF16(p->DefaultFont->Family); + hr = dwfactory->CreateTextFormat( + wDefaultFamily, NULL, + uiprivWeightToDWriteWeight(p->DefaultFont->Weight), + uiprivItalicToDWriteStyle(p->DefaultFont->Italic), + uiprivStretchToDWriteStretch(p->DefaultFont->Stretch), + pointSizeToDWriteSize(p->DefaultFont->Size), + // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx + // TODO use the current locale? + L"", + &(tl->format)); + uiFree(wDefaultFamily); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextFormat", hr); + hr = tl->format->SetTextAlignment(dwriteAligns[p->Align]); + if (hr != S_OK) + logHRESULT(L"error applying text layout alignment", hr); + + hr = dwfactory->CreateTextLayout( + (const WCHAR *) attrstrUTF16(p->String), attrstrUTF16Len(p->String), + tl->format, + // FLOAT is float, not double, so this should work... TODO + FLT_MAX, FLT_MAX, + &(tl->layout)); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextLayout", hr); + + // and set the width + // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway) + wrap = DWRITE_WORD_WRAPPING_WRAP; + maxWidth = (FLOAT) (p->Width); + if (p->Width < 0) { + // TODO is this wrapping juggling even necessary? + wrap = DWRITE_WORD_WRAPPING_NO_WRAP; + // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe + maxWidth = FLT_MAX; // see TODO above + } + hr = tl->layout->SetWordWrapping(wrap); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout word wrapping mode", hr); + hr = tl->layout->SetMaxWidth(maxWidth); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); + + attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundFuncs)); + + // and finally copy the UTF-8/UTF-16 index conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); + + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + uiFree(tl->u16tou8); + uiFree(tl->u8tou16); + delete tl->backgroundFuncs; + tl->layout->Release(); + tl->format->Release(); + uiFree(tl); +} + +static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush) +{ + D2D1_BRUSH_PROPERTIES props; + D2D1_COLOR_F color; + + ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); + props.opacity = 1.0; + // identity matrix + props.transform._11 = 1; + props.transform._22 = 1; + color.r = r; + color.g = g; + color.b = b; + color.a = a; + return rt->CreateSolidColorBrush( + &color, + &props, + brush); +} + +static ID2D1SolidColorBrush *mustMakeSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +{ + ID2D1SolidColorBrush *brush; + HRESULT hr; + + hr = mkSolidBrush(rt, r, g, b, a, &brush); + if (hr != S_OK) + logHRESULT(L"error creating solid brush", hr); + return brush; +} + +// some of the stuff we want to do isn't possible with what DirectWrite provides itself; we need to do it ourselves + +drawingEffectsAttr::drawingEffectsAttr(void) +{ + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; +} + +virtual HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid, void **ppvObject) +{ + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; +} + +virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::AddRef(void) +{ + this->refcount++; + return this->refcount; +} + +virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::Release(void) +{ + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; +} + +void drawingEffectsAttr::setColor(double r, double g, double b, double a) +{ + this->hasColor = true; + this->r = r; + this->g = g; + this->b = b; + this->a = a; +} + +void drawingEffectsAttr::setUnderline(uiUnderline u) +{ + this->hasUnderline = true; + this->u = u; +} + +void drawingEffectsAttr::setUnderlineColor(double r, double g, double b, double a) +{ + this->hasUnderlineColor = true; + this->ur = r; + this->ug = g; + this->ub = b; + this->ua = a; +} + +HRESULT drawingEffectsAttr::mkColorBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b) +{ + if (!this->hasColor) { + *b = NULL; + return S_OK; + } + return mkSolidBrush(rt, this->r, this->g, this->b, this->a, b); +} + +HRESULT drawingEffectsAttr::underline(uiUnderline *u) +{ + if (u == NULL) + return E_POINTER; + if (!this->hasUnderline) + return E_UNEXPECTED; + *u = this->u; + return S_OK; +} + +HRESULT drawingEffectsAttr::mkUnderlineBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b) +{ + if (!this->hasUnderlineColor) { + *b = NULL; + return S_OK; + } + return mkSolidBrush(rt, this->ur, this->ug, this->ub, this->ua, b); +} + +// this is based on http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html +class textRenderer : public IDWriteTextRenderer { + ULONG refcount; + ID2D1RenderTarget *rt; + BOOL snap; + ID2D1SolidColorBrush *black; +public: + textRenderer(ID2D1RenderTarget *rt, BOOL snap, ID2D1SolidColorBrush *black) + { + this->refcount = 1; + this->rt = rt; + this->snap = snap; + this->black = black; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown || + riid == __uuidof (IDWritePixelSnapping) || + riid == __uuidof (IDWriteTextRenderer)) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + // IDWritePixelSnapping + virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(void *clientDrawingContext, DWRITE_MATRIX *transform) + { + D2D1_MATRIX_3X2_F d2dtf; + + if (transform == NULL) + return E_POINTER; + this->rt->GetTransform(&d2dtf); + transform->m11 = d2dtf._11; + transform->m12 = d2dtf._12; + transform->m21 = d2dtf._21; + transform->m22 = d2dtf._22; + transform->dx = d2dtf._31; + transform->dy = d2dtf._32; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void *clientDrawingContext, FLOAT *pixelsPerDip) + { + FLOAT dpix, dpiy; + + if (pixelsPerDip == NULL) + return E_POINTER; + this->rt->GetDpi(&dpix, &dpiy); + *pixelsPerDip = dpix / 96; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void *clientDrawingContext, BOOL *isDisabled) + { + if (isDisabled == NULL) + return E_POINTER; + *isDisabled = !this->snap; + return S_OK; + } + + // IDWriteTextRenderer + virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) + { + D2D1_POINT_2F baseline; + drawingEffecsAttr *dea = (drawingEffectAttrs *) clientDrawingEffect; + ID2D1SolidColorBrush *brush; + + baseline.x = baselineOriginX; + baseline.y = baselineOriginY; + brush = NULL; + if (dea != NULL) { + HRESULT hr; + + hr = dea->mkColorBrush(this->rt, &brush); + if (hr != S_OK) + return hr; + } + if (brush == NULL) { + brush = this->black; + brush->Retain(); + } + this->rt->DrawGlyphRun( + baseline, + glyphRun, + brush, + measuringMode); + brush->Release(); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(void *clientDrawingContext, FLOAT originX, FLOAT originY, IDWriteInlineObject *inlineObject, BOOL isSideways, BOOL isRightToLeft, IUnknown *clientDrawingEffect) + { + if (inlineObject == NULL) + return E_POINTER; + return inlineObject->Draw(clientDrawingContext, this, + originX, originY, + isSideways, isRightToLeft, + clientDrawingEffect); + } + + virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) + { + // we don't support strikethrough + return E_UNEXPECTED; + } + + virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) + { + drawingEffectsAttr *dea = (drawingEffectsAttr *) clientDrawingEffect; + uiUnderline utype; + ID2D1SolidColorBrush *brush; + D2D1_RECT_F rect; + D2D1::Matrix3x2F pixeltf; + FLOAT dpix, dpiy; + D2D1_POINT_2F pt; + HRESULT hr; + + if (underline == NULL) + return E_POINTER; + if (dea == NULL) // we can only get here through an underline + return E_UNEXPECTED; + hr = dea->underline(&utype); + if (hr != S_OK) // we *should* only get here through an underline that's actually set... + return hr; + hr = dea->mkUnderlineBrush(this->rt, &brush); + if (hr != S_OK) + return hr; + if (brush == NULL) { + // TODO document this rule if not already done + hr = dea->mkColorBrush(this->rt, &brush); + if (hr != S_OK) + return hr; + } + if (brush == NULL) { + brush = this->black; + brush->Retain(); + } + rect.left = baselineOriginX; + rect.top = baselineOriginY + underline->offset; + rect.right = rect.left + underline->width; + rect.bottom = rect.top + underline->thickness; + switch (utype) { + case uiDrawUnderlineStyleSingle: + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleDouble: + // TODO do any of the matrix methods return errors? + // TODO standardize double-underline shape across platforms? wavy underline shape? + this->rt->GetTransform(&pixeltf); + this->rt->GetDpi(&dpix, &dpiy); + pixeltf = pixeltf * D2D1::Matrix3x2F::Scale(dpix / 96, dpiy / 96); + pt.x = 0; + pt.y = rect.top; + pt = pixeltf.TransformPoint(pt); + rect.top = (FLOAT) ((int) (pt.y + 0.5)); + pixeltf.Invert(); + pt = pixeltf.TransformPoint(pt); + rect.top = pt.y; + // first line + rect.top -= underline->thickness; + // and it seems we need to recompute this + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + // second line + rect.top += 2 * underline->thickness; + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleSuggestion: + { // TODO get rid of the extra block + // TODO properly clean resources on failure + // TODO use fully qualified C overloads for all methods + // TODO ensure all methods properly have errors handled + ID2D1PathGeometry *path; + ID2D1GeometrySink *sink; + double amplitude, period, xOffset, yOffset; + double t; + bool first = true; + HRESULT hr; + + hr = d2dfactory->CreatePathGeometry(&path); + if (hr != S_OK) + return hr; + hr = path->Open(&sink); + if (hr != S_OK) + return hr; + amplitude = underline->thickness; + period = 5 * underline->thickness; + xOffset = baselineOriginX; + yOffset = baselineOriginY + underline->offset; + for (t = 0; t < underline->width; t++) { + double x, angle, y; + D2D1_POINT_2F pt; + + x = t + xOffset; + angle = 2 * uiPi * fmod(x, period) / period; + y = amplitude * sin(angle) + yOffset; + pt.x = x; + pt.y = y; + if (first) { + sink->BeginFigure(pt, D2D1_FIGURE_BEGIN_HOLLOW); + first = false; + } else + sink->AddLine(pt); + } + sink->EndFigure(D2D1_FIGURE_END_OPEN); + hr = sink->Close(); + if (hr != S_OK) + return hr; + sink->Release(); + this->rt->DrawGeometry(path, brush, underline->thickness); + path->Release(); + } + break; + } + brush->Release(); + return S_OK; + } +}; + +$$$$ TODO continue here + +// TODO this ignores clipping? +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + D2D1_POINT_2F pt; + ID2D1SolidColorBrush *black; + textRenderer *renderer; + HRESULT hr; + + for (const auto &f : *(tl->backgroundFuncs)) + f(c, tl, x, y); + + // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms + // TODO figure out if this needs to be cleaned out + black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); + +#define renderD2D 0 +#define renderOur 1 +#if renderD2D + pt.x = x; + pt.y = y; + // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? + // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP? + // TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? + // TODO what is our pixel snapping setting related to the OPTIONS enum values? + c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); +#endif +#if renderD2D && renderOur + // draw ours semitransparent so we can check + // TODO get the actual color Charles Petzold uses and use that + black->Release(); + black = mustMakeSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); +#endif +#if renderOur + renderer = new textRenderer(c->rt, + TRUE, // TODO FALSE for no-snap? + black); + hr = tl->layout->Draw(NULL, + renderer, + x, y); + if (hr != S_OK) + logHRESULT(L"error drawing IDWriteTextLayout", hr); + renderer->Release(); +#endif + + black->Release(); +} + +// TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work... +// TODO width does not include trailing whitespace +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + DWRITE_TEXT_METRICS metrics; + HRESULT hr; + + hr = tl->layout->GetMetrics(&metrics); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout layout metrics", hr); + *width = metrics.width; + // TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too) + *height = metrics.height; +} + +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return tl->nLines; +} + +// DirectWrite doesn't provide a direct way to do this, so we have to do this manually +// TODO does that comment still apply here or to the code at the top of this file? +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + *start = tl->lineInfo[line].startPos; + *start = tl->u16tou8[*start]; + *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; + *end = tl->u16tou8[*end]; +} + +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) +{ + m->X = tl->lineInfo[line].x; + m->Y = tl->lineInfo[line].y; + m->Width = tl->lineInfo[line].width; + m->Height = tl->lineInfo[line].height; + + // TODO rename tl->lineInfo[line].baseline to .baselineOffset or something of the sort to make its meaning more clear + m->BaselineY = tl->lineInfo[line].y + tl->lineInfo[line].baseline; + m->Ascent = tl->lineInfo[line].baseline; + m->Descent = tl->lineInfo[line].height - tl->lineInfo[line].baseline; + m->Leading = 0; // TODO + + m->ParagraphSpacingBefore = 0; // TODO + m->LineHeightSpace = 0; // TODO + m->LineSpacing = 0; // TODO + m->ParagraphSpacing = 0; // TODO +} + +// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +// TODO go back through all of these and make sure we convert coordinates properly +// TODO same for OS X +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + DWRITE_HIT_TEST_METRICS m; + BOOL trailing, inside; + size_t p; + UINT32 i; + HRESULT hr; + + hr = tl->layout->HitTestPoint(x, y, + &trailing, &inside, + &m); + if (hr != S_OK) + logHRESULT(L"error hit-testing IDWriteTextLayout", hr); + p = m.textPosition; + // on a trailing hit, align to the nearest cluster + if (trailing) { + DWRITE_HIT_TEST_METRICS m2; + FLOAT x, y; // crashes if I skip these :/ + + hr = tl->layout->HitTestTextPosition(m.textPosition, trailing, + &x, &y, &m2); + if (hr != S_OK) + logHRESULT(L"error aligning trailing hit to nearest cluster", hr); + p = m2.textPosition + m2.length; + } + *pos = tl->u16tou8[p]; + + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineInfo[i].y; + lbottom = ltop + tl->lineInfo[i].height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; + } + if (i == tl->nLines) + i--; + *line = i; +} + +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + BOOL trailing; + DWRITE_HIT_TEST_METRICS m; + FLOAT x, y; + HRESULT hr; + + if (line < 0 || line >= tl->nLines) + return -1; + pos = tl->u8tou16[pos]; + // note: >, not >=, because the position at endPos is valid! + if (pos < tl->lineInfo[line].startPos || pos > tl->lineInfo[line].endPos) + return -1; + // this behavior seems correct + // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... + // TODO where does this fail? + trailing = FALSE; + if (pos != 0 && pos != tl->nUTF16 && pos == tl->lineInfo[line].endPos) { + pos--; + trailing = TRUE; + } + hr = tl->layout->HitTestTextPosition(pos, trailing, + &x, &y, &m); + if (hr != S_OK) + logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); + return x; +} + +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + DWORD caretWidth; + + // there seems to be no defined caret color + // the best I can come up with is "inverts colors underneath" (according to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648397(v=vs.85).aspx) which I have no idea how to do (TODO) + // just return black for now + p->r = 0.0; + p->g = 0.0; + p->b = 0.0; + p->a = 1.0; + + if (SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caretWidth, 0) == 0) + // don't log the failure, fall back gracefully + // the instruction to use this comes from https://msdn.microsoft.com/en-us/library/windows/desktop/ms648399(v=vs.85).aspx + // and we have to assume GetSystemMetrics() always succeeds, so + caretWidth = GetSystemMetrics(SM_CXBORDER); + // TODO make this a function and split it out of areautil.cpp + { + FLOAT dpix, dpiy; + + // TODO can we pass NULL for dpiy? + c->rt->GetDpi(&dpix, &dpiy); + // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd756649%28v=vs.85%29.aspx (and others; search "direct2d mouse") + p->width = ((double) (caretWidth * 96)) / dpix; + } + // and there doesn't seem to be this either... (TODO check what PadWrite does?) + p->xoff = 0; +} From 1b1d609c88cff15fd9febd49a9ffe5823e2ffb39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 14 Mar 2018 21:08:19 -0400 Subject: [PATCH 423/487] More migrations of attrstr.cpp and drawtext.cpp. My this is a mess :D --- windows/OLD_uipriv_attrstr.h | 4 - windows/attrstr.cpp | 40 ++++------ windows/attrstr.hpp | 13 +++- windows/drawtext.cpp | 145 ++++------------------------------- 4 files changed, 45 insertions(+), 157 deletions(-) diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index a183b3b0..c494a59e 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -23,7 +23,3 @@ extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); extern void loadInitialFontDialogParams(struct fontDialogParams *params); extern void destroyFontDialogParams(struct fontDialogParams *params); extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); - -// attrstr.cpp -typedef std::function backgroundFunc; -extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index a1455b71..586b1f4d 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -4,14 +4,15 @@ // TODO this whole file needs cleanup -// we need to collect all the background blocks and add them all at once +// we need to collect all the background parameters and add them all at once +// TODO consider having background parameters in the drawing effects // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; size_t len; IDWriteTextLayout *layout; - std::vector *backgroundFuncs; + std::vector *backgroundParams; }; static std::hash doubleHash; @@ -255,18 +256,15 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, return S_OK; } -static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) +static void addBackgroundParams(struct foreachParams *p, size_t start, size_t end, const uiAttribute *attr) { - return [=](uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { - uiDrawBrush brush; + struct drawTextBackgroundParams *params; - brush.Type = uiDrawBrushTypeSolid; - brush.R = r; - brush.G = g; - brush.B = b; - brush.A = a; - drawTextBackground(c, x, y, layout, start, end, &brush, 0); - }; + params = uiprivNew(struct drawTextBackgroundParams); + params->start = start; + params->end = end; + uiAttributeColor(attr, &(params->r), &(params->g), &(params->b), &(params->a)); + p->backgroundParams->push_back(params); } static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) @@ -274,16 +272,12 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; WCHAR *wfamily; - size_t ostart, oend; BOOL hasUnderline; IDWriteTypography *dt; HRESULT hr; - ostart = start; - oend = end; - // TODO fix const correctness - start = attrstrUTF8ToUTF16((uiAttributedString *) s, start); - end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); + start = uiprivAttributedStringUTF8ToUTF16(s, start); + end = uiprivAttributedStringUTF8ToUTF16(s, end); range.startPosition = start; range.length = end - start; switch (uiAttributeGetType(attr)) { @@ -340,9 +334,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute logHRESULT(L"error applying effect (color, underline, or underline color) attribute", hr); break; case uiAttributeBackground: - p->backgroundFuncs->push_back( - mkBackgroundFunc(ostart, oend, - spec->R, spec->G, spec->B, spec->A)); + addBackgroundParams(p, start, end, attr); break; case uiAttributeTypeFeatures: // only generate an attribute if not NULL @@ -407,7 +399,7 @@ TODO return S_OK; } -void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) +void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundParams) { struct foreachParams fep; HRESULT hr; @@ -415,10 +407,10 @@ void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutPar fep.s = attrstrUTF16(p->String); fep.len = attrstrUTF16Len(p->String); fep.layout = layout; - fep.backgroundFuncs = new std::vector; + fep.backgroundParams = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); hr = applyEffectsAttributes(&fep); if (hr != S_OK) logHRESULT(L"error applying effects attributes", hr); - *backgroundFuncs = fep.backgroundFuncs; + *backgroundParams = fep.backgroundParams; } diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 6c0bce7f..828223eb 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -13,7 +13,9 @@ extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc); // attrstr.cpp -extern void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); +// TODO +struct drawTextBackgroundParams; +extern void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); // drawtext.cpp class drawingEffectsAttr : public IUnknown { @@ -48,3 +50,12 @@ public: HRESULT underline(uiUnderline *u); HRESULT mkUnderlineBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b); }; +// TODO figure out where this type should *really* go in all the headers... +struct drawTextBackgroundParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index c9baef49..c1de9b2e 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -8,7 +8,7 @@ struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; - std::vector *backgroundFuncs; + std::vector *backgroundParams; // for converting DirectWrite indices from/to byte offsets size_t *u8tou16; size_t nUTF8; @@ -84,7 +84,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); - attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundFuncs)); + attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundParams)); // and finally copy the UTF-8/UTF-16 index conversion tables tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); @@ -97,12 +97,15 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); uiFree(tl->u8tou16); - delete tl->backgroundFuncs; + for (auto p in *(tl->backgroundParams)) + uiprivFree(p); + delete tl->backgroundParams; tl->layout->Release(); tl->format->Release(); uiFree(tl); } +// TODO make this shared code somehow static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush) { D2D1_BRUSH_PROPERTIES props; @@ -354,6 +357,7 @@ public: return E_UNEXPECTED; } + // TODO clean this function up virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { drawingEffectsAttr *dea = (drawingEffectsAttr *) clientDrawingEffect; @@ -468,8 +472,6 @@ public: } }; -$$$$ TODO continue here - // TODO this ignores clipping? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { @@ -478,8 +480,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) textRenderer *renderer; HRESULT hr; - for (const auto &f : *(tl->backgroundFuncs)) - f(c, tl, x, y); + for (auto p : *(tl->backgroundParams)) { + // TODO + } // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out @@ -534,136 +537,22 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { +return 0; +#if 0 +TODO return tl->nLines; +#endif } // DirectWrite doesn't provide a direct way to do this, so we have to do this manually // TODO does that comment still apply here or to the code at the top of this file? void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { +#if 0 +TODO *start = tl->lineInfo[line].startPos; *start = tl->u16tou8[*start]; *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; *end = tl->u16tou8[*end]; -} - -void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) -{ - m->X = tl->lineInfo[line].x; - m->Y = tl->lineInfo[line].y; - m->Width = tl->lineInfo[line].width; - m->Height = tl->lineInfo[line].height; - - // TODO rename tl->lineInfo[line].baseline to .baselineOffset or something of the sort to make its meaning more clear - m->BaselineY = tl->lineInfo[line].y + tl->lineInfo[line].baseline; - m->Ascent = tl->lineInfo[line].baseline; - m->Descent = tl->lineInfo[line].height - tl->lineInfo[line].baseline; - m->Leading = 0; // TODO - - m->ParagraphSpacingBefore = 0; // TODO - m->LineHeightSpace = 0; // TODO - m->LineSpacing = 0; // TODO - m->ParagraphSpacing = 0; // TODO -} - -// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() -// TODO go back through all of these and make sure we convert coordinates properly -// TODO same for OS X -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) -{ - DWRITE_HIT_TEST_METRICS m; - BOOL trailing, inside; - size_t p; - UINT32 i; - HRESULT hr; - - hr = tl->layout->HitTestPoint(x, y, - &trailing, &inside, - &m); - if (hr != S_OK) - logHRESULT(L"error hit-testing IDWriteTextLayout", hr); - p = m.textPosition; - // on a trailing hit, align to the nearest cluster - if (trailing) { - DWRITE_HIT_TEST_METRICS m2; - FLOAT x, y; // crashes if I skip these :/ - - hr = tl->layout->HitTestTextPosition(m.textPosition, trailing, - &x, &y, &m2); - if (hr != S_OK) - logHRESULT(L"error aligning trailing hit to nearest cluster", hr); - p = m2.textPosition + m2.length; - } - *pos = tl->u16tou8[p]; - - for (i = 0; i < tl->nLines; i++) { - double ltop, lbottom; - - ltop = tl->lineInfo[i].y; - lbottom = ltop + tl->lineInfo[i].height; - // y will already >= ltop at this point since the past lbottom should == ltop - if (y < lbottom) - break; - } - if (i == tl->nLines) - i--; - *line = i; -} - -double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) -{ - BOOL trailing; - DWRITE_HIT_TEST_METRICS m; - FLOAT x, y; - HRESULT hr; - - if (line < 0 || line >= tl->nLines) - return -1; - pos = tl->u8tou16[pos]; - // note: >, not >=, because the position at endPos is valid! - if (pos < tl->lineInfo[line].startPos || pos > tl->lineInfo[line].endPos) - return -1; - // this behavior seems correct - // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... - // TODO where does this fail? - trailing = FALSE; - if (pos != 0 && pos != tl->nUTF16 && pos == tl->lineInfo[line].endPos) { - pos--; - trailing = TRUE; - } - hr = tl->layout->HitTestTextPosition(pos, trailing, - &x, &y, &m); - if (hr != S_OK) - logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); - return x; -} - -void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) -{ - DWORD caretWidth; - - // there seems to be no defined caret color - // the best I can come up with is "inverts colors underneath" (according to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648397(v=vs.85).aspx) which I have no idea how to do (TODO) - // just return black for now - p->r = 0.0; - p->g = 0.0; - p->b = 0.0; - p->a = 1.0; - - if (SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caretWidth, 0) == 0) - // don't log the failure, fall back gracefully - // the instruction to use this comes from https://msdn.microsoft.com/en-us/library/windows/desktop/ms648399(v=vs.85).aspx - // and we have to assume GetSystemMetrics() always succeeds, so - caretWidth = GetSystemMetrics(SM_CXBORDER); - // TODO make this a function and split it out of areautil.cpp - { - FLOAT dpix, dpiy; - - // TODO can we pass NULL for dpiy? - c->rt->GetDpi(&dpix, &dpiy); - // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd756649%28v=vs.85%29.aspx (and others; search "direct2d mouse") - p->width = ((double) (caretWidth * 96)) / dpix; - } - // and there doesn't seem to be this either... (TODO check what PadWrite does?) - p->xoff = 0; +#endif } From 1d1b6c316290bb70474e4017d6f57bff250efa7f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 16 Mar 2018 20:06:23 -0400 Subject: [PATCH 424/487] Migrated fontbutton.cpp back. --- windows/OLD_uipriv_attrstr.h | 11 ----------- windows/attrstr.hpp | 12 ++++++++++++ windows/{OLD_fontbutton.cpp => fontbutton.cpp} | 13 +++++++------ 3 files changed, 19 insertions(+), 17 deletions(-) rename windows/{OLD_fontbutton.cpp => fontbutton.cpp} (90%) diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index c494a59e..83c73b1f 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -12,14 +12,3 @@ extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *fa extern void fontCollectionFree(fontCollection *fc); extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); -// fontdialog.cpp -struct fontDialogParams { - IDWriteFont *font; - double size; - WCHAR *familyName; - WCHAR *styleName; -}; -extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); -extern void loadInitialFontDialogParams(struct fontDialogParams *params); -extern void destroyFontDialogParams(struct fontDialogParams *params); -extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 828223eb..df143613 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -59,3 +59,15 @@ struct drawTextBackgroundParams { double b; double a; }; + +// fontdialog.cpp +struct fontDialogParams { + IDWriteFont *font; + double size; + WCHAR *familyName; + WCHAR *styleName; +}; +extern BOOL uiprivShowFontDialog(HWND parent, struct fontDialogParams *params); +extern void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params); +extern void uiprivDestroyFontDialogParams(struct fontDialogParams *params); +extern WCHAR *uiprivFontDialogParamsToString(struct fontDialogParams *params); diff --git a/windows/OLD_fontbutton.cpp b/windows/fontbutton.cpp similarity index 90% rename from windows/OLD_fontbutton.cpp rename to windows/fontbutton.cpp index ab9fbe73..4f1ef594 100644 --- a/windows/OLD_fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -1,5 +1,6 @@ // 14 april 2016 #include "uipriv_windows.hpp" +#include "attrstr.hpp" struct uiFontButton { uiWindowsControl c; @@ -15,7 +16,7 @@ static void uiFontButtonDestroy(uiControl *c) uiFontButton *b = uiFontButton(c); uiWindowsUnregisterWM_COMMANDHandler(b->hwnd); - destroyFontDialogParams(&(b->params)); + uiprivDestroyFontDialogParams(&(b->params)); uiWindowsEnsureDestroyWindow(b->hwnd); uiFreeControl(uiControl(b)); } @@ -24,9 +25,9 @@ static void updateFontButtonLabel(uiFontButton *b) { WCHAR *text; - text = fontDialogParamsToString(&(b->params)); + text = uiprivFontDialogParamsToString(&(b->params)); setWindowText(b->hwnd, text); - uiFree(text); + uiprivFree(text); // changing the text might necessitate a change in the button's size uiWindowsControlMinimumSizeChanged(uiWindowsControl(b)); @@ -41,7 +42,7 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) return FALSE; parent = parentToplevel(b->hwnd); - if (showFontDialog(parent, &(b->params))) { + if (uiprivShowFontDialog(parent, &(b->params))) { updateFontButtonLabel(b); (*(b->onChanged))(b, b->onChangedData); } @@ -88,7 +89,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - fontdescFromIDWriteFont(b->params.font, desc); + uiprivFontDescriptorFromIDWriteFont(b->params.font, desc); desc->Family = toUTF8(b->params.familyName); desc->Size = b->params.size; } @@ -111,7 +112,7 @@ uiFontButton *uiNewFontButton(void) hInstance, NULL, TRUE); - loadInitialFontDialogParams(&(b->params)); + uiprivLoadInitialFontDialogParams(&(b->params)); uiWindowsRegisterWM_COMMANDHandler(b->hwnd, onWM_COMMAND, uiControl(b)); uiFontButtonOnChanged(b, defaultOnChanged, NULL); From 8352cd72b8ae3b35fc1d70d873a821e8cd9362ee Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:44:38 -0400 Subject: [PATCH 425/487] "Migrated" dwrite.cpp back. Not much to say here; it'll do for now. --- windows/OLD_uipriv_attrstr.h | 13 ------------- windows/attrstr.hpp | 14 +++++++++++++ windows/{OLD_dwrite.cpp => dwrite.cpp} | 27 +++++++++++++------------- 3 files changed, 28 insertions(+), 26 deletions(-) rename windows/{OLD_dwrite.cpp => dwrite.cpp} (84%) diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index 83c73b1f..8b137891 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -1,14 +1 @@ -// dwrite.cpp -extern IDWriteFactory *dwfactory; -extern HRESULT initDrawText(void); -extern void uninitDrawText(void); -struct fontCollection { - IDWriteFontCollection *fonts; - WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; - int userLocaleSuccess; -}; -extern fontCollection *loadFontCollection(void); -extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); -extern void fontCollectionFree(fontCollection *fc); -extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index df143613..449000ed 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -3,6 +3,20 @@ extern "C" { #include "../common/attrstr.h" } +// dwrite.cpp +extern IDWriteFactory *dwfactory; +extern HRESULT uiprivInitDrawText(void); +extern void uiprivUninitDrawText(void); +struct fontCollection { + IDWriteFontCollection *fonts; + WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; + int userLocaleSuccess; +}; +extern fontCollection *uiprivLoadFontCollection(void); +extern WCHAR *uiprivFontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); +extern void uiprivFontCollectionFree(fontCollection *fc); +extern WCHAR *uiprivFontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); + // opentype.cpp extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf); diff --git a/windows/OLD_dwrite.cpp b/windows/dwrite.cpp similarity index 84% rename from windows/OLD_dwrite.cpp rename to windows/dwrite.cpp index 498ef376..3aeedfdb 100644 --- a/windows/OLD_dwrite.cpp +++ b/windows/dwrite.cpp @@ -1,10 +1,11 @@ // 14 april 2016 #include "uipriv_windows.hpp" -// TODO really migrate? (TODO what did I mean by this?) +#include "attrstr.hpp" IDWriteFactory *dwfactory = NULL; -HRESULT initDrawText(void) +// TOOD rename to something else, maybe +HRESULT uiprivInitDrawText(void) { // TOOD use DWRITE_FACTORY_TYPE_ISOLATED instead? return DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, @@ -12,17 +13,17 @@ HRESULT initDrawText(void) (IUnknown **) (&dwfactory)); } -void uninitDrawText(void) +void uiprivUninitDrawText(void) { dwfactory->Release(); } -fontCollection *loadFontCollection(void) +fontCollection *uiprivLoadFontCollection(void) { fontCollection *fc; HRESULT hr; - fc = uiNew(fontCollection); + fc = uiprivNew(fontCollection); // always get the latest available font information hr = dwfactory->GetSystemFontCollection(&(fc->fonts), TRUE); if (hr != S_OK) @@ -31,7 +32,13 @@ fontCollection *loadFontCollection(void) return fc; } -WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) +void uiprivFontCollectionFree(fontCollection *fc) +{ + fc->fonts->Release(); + uiprivFree(fc); +} + +WCHAR *uiprivFontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) { IDWriteLocalizedStrings *names; WCHAR *str; @@ -45,7 +52,7 @@ WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) return str; } -WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names) +WCHAR *uiprivFontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names) { UINT32 index; BOOL exists; @@ -81,9 +88,3 @@ WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings * return wname; } - -void fontCollectionFree(fontCollection *fc) -{ - fc->fonts->Release(); - uiFree(fc); -} From e08460adc3bfa1c4b9fea0249f285ff3014d1ea2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:45:45 -0400 Subject: [PATCH 426/487] Cleanup from the previous commit. --- windows/OLD_uipriv_attrstr.h | 1 - windows/init.cpp | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 windows/OLD_uipriv_attrstr.h diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h deleted file mode 100644 index 8b137891..00000000 --- a/windows/OLD_uipriv_attrstr.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/windows/init.cpp b/windows/init.cpp index 22874165..cfe63b9a 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -1,5 +1,6 @@ // 6 april 2015 #include "uipriv_windows.hpp" +#include "attrstr.hpp" HINSTANCE hInstance; int nCmdShow; @@ -117,7 +118,7 @@ const char *uiInit(uiInitOptions *o) if (hr != S_OK) return ieHRESULT("initializing Direct2D", hr); - hr = initDrawText(); + hr = uiprivInitDrawText(); if (hr != S_OK) return ieHRESULT("initializing DirectWrite", hr); @@ -139,7 +140,7 @@ void uiUninit(void) unregisterD2DScratchClass(); unregisterMessageFilter(); unregisterArea(); - uninitDrawText(); + uiprivUninitDrawText(); uninitDraw(); CoUninitialize(); if (DeleteObject(hollowBrush) == 0) From cdaf49ec3017bdd58d7c9c5537606768ccb1328b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:47:20 -0400 Subject: [PATCH 427/487] And quick and dirty migration of fontdialog.cpp back. Okay, after fixing the CMakeLists.txt, let's see how bad this is. --- windows/{OLD_fontdialog.cpp => fontdialog.cpp} | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) rename windows/{OLD_fontdialog.cpp => fontdialog.cpp} (98%) diff --git a/windows/OLD_fontdialog.cpp b/windows/fontdialog.cpp similarity index 98% rename from windows/OLD_fontdialog.cpp rename to windows/fontdialog.cpp index 6096d442..6a137de1 100644 --- a/windows/OLD_fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -1,5 +1,6 @@ // 14 april 2016 #include "uipriv_windows.hpp" +#include "attrstr.hpp" // TODOs // - quote the Choose Font sample here for reference @@ -590,7 +591,7 @@ static INT_PTR CALLBACK fontDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, L return FALSE; } -BOOL showFontDialog(HWND parent, struct fontDialogParams *params) +BOOL uiprivShowFontDialog(HWND parent, struct fontDialogParams *params) { switch (DialogBoxParamW(hInstance, MAKEINTRESOURCE(rcFontDialog), parent, fontDialogDlgProc, (LPARAM) params)) { case 1: // cancel @@ -622,7 +623,7 @@ static IDWriteFontFamily *tryFindFamily(IDWriteFontCollection *fc, const WCHAR * return family; } -void loadInitialFontDialogParams(struct fontDialogParams *params) +void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params) { struct fontCollection *fc; IDWriteFontFamily *family; @@ -668,14 +669,14 @@ void loadInitialFontDialogParams(struct fontDialogParams *params) fontCollectionFree(fc); } -void destroyFontDialogParams(struct fontDialogParams *params) +void uiprivDestroyFontDialogParams(struct fontDialogParams *params) { params->font->Release(); - uiFree(params->familyName); - uiFree(params->styleName); + uiprivFree(params->familyName); + uiprivFree(params->styleName); } -WCHAR *fontDialogParamsToString(struct fontDialogParams *params) +WCHAR *uiprivFontDialogParamsToString(struct fontDialogParams *params) { WCHAR *text; From 51952b45994fa882b079d2f70622b78bbf62c2e0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:50:10 -0400 Subject: [PATCH 428/487] And fixed the CMakeLists.txt. Now to build. --- windows/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 739c1300..eab9ca90 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -29,6 +29,7 @@ list(APPEND _LIBUI_SOURCES windows/events.cpp windows/fontbutton.cpp windows/fontdialog.cpp + windows/fontmatch.cpp windows/form.cpp windows/graphemes.cpp windows/grid.cpp From 2f0283618109b967d91179c85788649495fdca34 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 15:29:06 -0400 Subject: [PATCH 429/487] Fixed attrstr.cpp. Wow, that unordered_map custom hash and equal_to actually compiles! --- windows/attrstr.cpp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 586b1f4d..c39a259e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -4,6 +4,14 @@ // TODO this whole file needs cleanup +// yep, even when it supports C++11, it doesn't support C++11 +// we require MSVC 2013; this was added in MSVC 2015 (https://msdn.microsoft.com/en-us/library/wfa0edys.aspx) +#ifdef _MSC_VER +#if _MSC_VER < 1900 +#define noexcept +#endif +#endif + // we need to collect all the background parameters and add them all at once // TODO consider having background parameters in the drawing effects // TODO contextual alternates override ligatures? @@ -53,7 +61,7 @@ class combinedEffectsAttr : public IUnknown { // this is needed by applyEffectsAttributes() below // TODO doesn't uiprivAttributeEqual() already do this; if it doesn't, make it so; if (or when) it does, fix all platforms to avoid this extra check - static bool attrEqual(uiAttribute *a, uiAttribute *b) const + static bool attrEqual(uiAttribute *a, uiAttribute *b) { if (a == NULL && b == NULL) return true; @@ -127,6 +135,7 @@ public: { size_t ret = 0; double r, g, b, a; + uiUnderlineColor colorType; if (this->colorAttr != NULL) { uiAttributeColor(this->colorAttr, &r, &g, &b, &a); @@ -152,9 +161,9 @@ public: { if (b == NULL) return false; - return combinedEffectsAttr::attrEqual(a->colorAttr, b->colorAttr) && - combinedEffectsAttr::attrEqual(a->underilneAttr, b->underlineAttr) && - combinedEffectsAttr::attrEqual(a->underlineColorAttr, b->underlineColorAttr); + return combinedEffectsAttr::attrEqual(this->colorAttr, b->colorAttr) && + combinedEffectsAttr::attrEqual(this->underlineAttr, b->underlineAttr) && + combinedEffectsAttr::attrEqual(this->underlineColorAttr, b->underlineColorAttr); } drawingEffectsAttr *toDrawingEffectsAttr(void) @@ -166,12 +175,12 @@ public: dea = new drawingEffectsAttr; if (this->colorAttr != NULL) { uiAttributeColor(this->colorAttr, &r, &g, &b, &a); - dea->addColor(r, g, b, a); + dea->setColor(r, g, b, a); } if (this->underlineAttr != NULL) - dea->addUnderline(uiAttributeUnderline(this->underlineAttr)); + dea->setUnderline(uiAttributeUnderline(this->underlineAttr)); if (this->underlineColorAttr != NULL) { - uiAttributeUnderlineColor(this->underlineColor, &colorType, &r, &g, &b, &a); + uiAttributeUnderlineColor(this->underlineColorAttr, &colorType, &r, &g, &b, &a); // TODO see if Microsoft has any standard colors for these switch (colorType) { case uiUnderlineColorSpelling: @@ -187,14 +196,14 @@ public: b = 0.0; a = 1.0; break; - case uiUnderlineColorAlternate: + case uiUnderlineColorAuxiliary: r = 0.0; g = 0.0; b = 1.0; a = 1.0; break; } - dea->addUnderlineColor(r, g, b, a); + dea->setUnderlineColor(r, g, b, a); } return dea; } @@ -329,11 +338,12 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute // and fall through to set the underline style through the drawing effect case uiAttributeTypeColor: case uiAttributeTypeUnderlineColor: - hr = addEffectAttributeToRange(p, start, end, attr); + // TODO const-correct this properly + hr = addEffectAttributeToRange(p, start, end, (uiAttribute *) attr); if (hr != S_OK) logHRESULT(L"error applying effect (color, underline, or underline color) attribute", hr); break; - case uiAttributeBackground: + case uiAttributeTypeBackground: addBackgroundParams(p, start, end, attr); break; case uiAttributeTypeFeatures: @@ -360,7 +370,7 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) // here's the magic: this std::unordered_map will deduplicate all of our combinedEffectsAttrs, mapping all identical ones to a single drawingEffectsAttr // because drawingEffectsAttr is the *actual* drawing effect we want for rendering, we also replace the combinedEffectsAttrs with them in the IDWriteTextLayout at the same time // note the use of our custom hash and equal_to implementations - std::unordered_map effects; HRESULT hr; @@ -378,7 +388,7 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) dea = diter->second; else { dea = cea->toDrawingEffectsAttr(); - effects.insert(cea, dea); + effects.insert({cea, dea}); } hr = p->layout->SetDrawingEffect(dea, range); if (hr != S_OK) @@ -404,8 +414,8 @@ void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutPar struct foreachParams fep; HRESULT hr; - fep.s = attrstrUTF16(p->String); - fep.len = attrstrUTF16Len(p->String); + fep.s = uiprivAttributedStringUTF16String(p->String); + fep.len = uiprivAttributedStringUTF16Len(p->String); fep.layout = layout; fep.backgroundParams = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); From a5bbc83359f92fbbc86f219a8a8bcf7b9e2f7eb2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 15:49:00 -0400 Subject: [PATCH 430/487] Fixed build errors. Now to test. --- common/attrstr.h | 4 ++++ windows/attrstr.cpp | 1 + windows/drawtext.cpp | 28 ++++++++++++++-------------- windows/dwrite.cpp | 2 +- windows/fontbutton.cpp | 2 +- windows/fontdialog.cpp | 22 +++++++++++----------- windows/fontmatch.cpp | 6 +++--- windows/opentype.cpp | 3 ++- 8 files changed, 37 insertions(+), 31 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index 643c8c3c..fb2346d1 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,7 +5,11 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) +#ifndef _WIN32 #define uiprivStricmp(x, y) strcasecmp(x, y) +#else +#define uiprivStricmp(x, y) stricmp(x, y) +#endif // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index c39a259e..feaaa61e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -210,6 +210,7 @@ public: }; // also needed by applyEffectsAttributes() below +// TODO provide all the fields of std::hash and std::equal_to? class applyEffectsHash { public: typedef combinedEffectsAttr *ceaptr; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index c1de9b2e..56a7dfbb 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -59,7 +59,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) logHRESULT(L"error applying text layout alignment", hr); hr = dwfactory->CreateTextLayout( - (const WCHAR *) attrstrUTF16(p->String), attrstrUTF16Len(p->String), + (const WCHAR *) uiprivAttributedStringUTF16String(p->String), uiprivAttributedStringUTF16Len(p->String), tl->format, // FLOAT is float, not double, so this should work... TODO FLT_MAX, FLT_MAX, @@ -84,11 +84,11 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); - attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundParams)); + uiprivAttributedStringApplyAttributesToDWriteTextLayout(p, tl->layout, &(tl->backgroundParams)); // and finally copy the UTF-8/UTF-16 index conversion tables - tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); - tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); + tl->u8tou16 = uiprivAttributedStringCopyUTF8ToUTF16Table(p->String, &(tl->nUTF8)); + tl->u16tou8 = uiprivAttributedStringCopyUTF16ToUTF8Table(p->String, &(tl->nUTF16)); return tl; } @@ -97,7 +97,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); uiFree(tl->u8tou16); - for (auto p in *(tl->backgroundParams)) + for (auto p : *(tl->backgroundParams)) uiprivFree(p); delete tl->backgroundParams; tl->layout->Release(); @@ -147,7 +147,7 @@ drawingEffectsAttr::drawingEffectsAttr(void) this->hasUnderlineColor = false; } -virtual HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid, void **ppvObject) +HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid, void **ppvObject) { if (ppvObject == NULL) return E_POINTER; @@ -160,13 +160,13 @@ virtual HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid return E_NOINTERFACE; } -virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::AddRef(void) +ULONG STDMETHODCALLTYPE drawingEffectsAttr::AddRef(void) { this->refcount++; return this->refcount; } -virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::Release(void) +ULONG STDMETHODCALLTYPE drawingEffectsAttr::Release(void) { this->refcount--; if (this->refcount == 0) { @@ -315,7 +315,7 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) { D2D1_POINT_2F baseline; - drawingEffecsAttr *dea = (drawingEffectAttrs *) clientDrawingEffect; + drawingEffectsAttr *dea = (drawingEffectsAttr *) clientDrawingEffect; ID2D1SolidColorBrush *brush; baseline.x = baselineOriginX; @@ -330,7 +330,7 @@ public: } if (brush == NULL) { brush = this->black; - brush->Retain(); + brush->AddRef(); } this->rt->DrawGlyphRun( baseline, @@ -387,17 +387,17 @@ public: } if (brush == NULL) { brush = this->black; - brush->Retain(); + brush->AddRef(); } rect.left = baselineOriginX; rect.top = baselineOriginY + underline->offset; rect.right = rect.left + underline->width; rect.bottom = rect.top + underline->thickness; switch (utype) { - case uiDrawUnderlineStyleSingle: + case uiUnderlineSingle: this->rt->FillRectangle(&rect, brush); break; - case uiDrawUnderlineStyleDouble: + case uiUnderlineDouble: // TODO do any of the matrix methods return errors? // TODO standardize double-underline shape across platforms? wavy underline shape? this->rt->GetTransform(&pixeltf); @@ -420,7 +420,7 @@ public: rect.bottom = rect.top + underline->thickness; this->rt->FillRectangle(&rect, brush); break; - case uiDrawUnderlineStyleSuggestion: + case uiUnderlineSuggestion: { // TODO get rid of the extra block // TODO properly clean resources on failure // TODO use fully qualified C overloads for all methods diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 3aeedfdb..24a4aa3a 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -47,7 +47,7 @@ WCHAR *uiprivFontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *fam hr = family->GetFamilyNames(&names); if (hr != S_OK) logHRESULT(L"error getting names of font out", hr); - str = fontCollectionCorrectString(fc, names); + str = uiprivFontCollectionCorrectString(fc, names); names->Release(); return str; } diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index 4f1ef594..29de64b6 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -87,7 +87,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) +void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc) { uiprivFontDescriptorFromIDWriteFont(b->params.font, desc); desc->Family = toUTF8(b->params.familyName); diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index 6a137de1..2d10a1ae 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -18,7 +18,7 @@ struct fontDialog { struct fontDialogParams *params; - fontCollection *fc; + struct fontCollection *fc; RECT sampleRect; HWND sampleBox; @@ -169,7 +169,7 @@ static WCHAR *fontStyleName(struct fontCollection *fc, IDWriteFont *font) hr = font->GetFaceNames(&str); if (hr != S_OK) logHRESULT(L"error getting font style name for font dialog", hr); - wstr = fontCollectionCorrectString(fc, str); + wstr = uiprivFontCollectionCorrectString(fc, str); str->Release(); return wstr; } @@ -365,7 +365,7 @@ static void fontDialogDrawSampleText(struct fontDialog *f, ID2D1RenderTarget *rt if (hr != S_OK) exists = FALSE; if (exists) { - sample = fontCollectionCorrectString(f->fc, sampleStrings); + sample = uiprivFontCollectionCorrectString(f->fc, sampleStrings); sampleStrings->Release(); } else sample = L"The quick brown fox jumps over the lazy dog."; @@ -474,13 +474,13 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) f->styleCombobox = getDlgItem(f->hwnd, rcFontStyleCombobox); f->sizeCombobox = getDlgItem(f->hwnd, rcFontSizeCombobox); - f->fc = loadFontCollection(); + f->fc = uiprivLoadFontCollection(); nFamilies = f->fc->fonts->GetFontFamilyCount(); for (i = 0; i < nFamilies; i++) { hr = f->fc->fonts->GetFontFamily(i, &family); if (hr != S_OK) logHRESULT(L"error getting font family", hr); - wname = fontCollectionFamilyName(f->fc, family); + wname = uiprivFontCollectionFamilyName(f->fc, family); pos = cbAddString(f->familyCombobox, wname); uiFree(wname); cbSetItemData(f->familyCombobox, (WPARAM) pos, (LPARAM) family); @@ -503,7 +503,7 @@ static void endFontDialog(struct fontDialog *f, INT_PTR code) { wipeStylesBox(f); cbWipeAndReleaseData(f->familyCombobox); - fontCollectionFree(f->fc); + uiprivFontCollectionFree(f->fc); if (EndDialog(f->hwnd, code) == 0) logLastError(L"error ending font dialog"); uiFree(f); @@ -520,13 +520,13 @@ static INT_PTR tryFinishDialog(struct fontDialog *f, WPARAM wParam) } // OK - destroyFontDialogParams(f->params); + uiprivDestroyFontDialogParams(f->params); f->params->font = (IDWriteFont *) cbGetItemData(f->styleCombobox, f->curStyle); // we need to save font from being destroyed with the combobox f->params->font->AddRef(); f->params->size = f->curSize; family = (IDWriteFontFamily *) cbGetItemData(f->familyCombobox, f->curFamily); - f->params->familyName = fontCollectionFamilyName(f->fc, family); + f->params->familyName = uiprivFontCollectionFamilyName(f->fc, family); f->params->styleName = fontStyleName(f->fc, f->params->font); endFontDialog(f, 2); return TRUE; @@ -636,7 +636,7 @@ void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params) // If Arial isn't found, we'll use Helvetica and then MS Sans Serif as fallbacks, and if not, we'll just grab the first font family in the collection. // We need the correct localized name for Regular (and possibly Arial too? let's say yes to be safe), so let's grab the strings from DirectWrite instead of hardcoding them. - fc = loadFontCollection(); + fc = uiprivLoadFontCollection(); family = tryFindFamily(fc->fonts, L"Arial"); if (family == NULL) { family = tryFindFamily(fc->fonts, L"Helvetica"); @@ -661,12 +661,12 @@ void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params) params->font = font; params->size = 10; - params->familyName = fontCollectionFamilyName(fc, family); + params->familyName = uiprivFontCollectionFamilyName(fc, family); params->styleName = fontStyleName(fc, font); // don't release font; we still need it family->Release(); - fontCollectionFree(fc); + uiprivFontCollectionFree(fc); } void uiprivDestroyFontDialogParams(struct fontDialogParams *params) diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp index 28b4c84a..73f29543 100644 --- a/windows/fontmatch.cpp +++ b/windows/fontmatch.cpp @@ -49,13 +49,13 @@ void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *ui dwitalic = font->GetStyle(); // TODO reverse the above misalignment if it is corrected - uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); + uidesc->Weight = (uiTextWeight) (font->GetWeight()); dwstretch = font->GetStretch(); - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + for (uidesc->Italic = uiTextItalicNormal; uidesc->Italic < uiTextItalicItalic; uidesc->Italic++) if (dwriteItalics[uidesc->Italic] == dwitalic) break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + for (uidesc->Stretch = uiTextStretchUltraCondensed; uidesc->Stretch < uiTextStretchUltraExpanded; uidesc->Stretch++) if (dwriteStretches[uidesc->Stretch] == dwstretch) break; } diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 0a96cdc4..777f30d6 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -11,7 +11,8 @@ static uiForEach addToTypography(const uiOpenTypeFeatures *otf, char a, char b, HRESULT hr; ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); - dff.nameTag = /*(DWRITE_FONT_FEATURE_TAG)*/ DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d); + // yes, the cast here is necessary (the compiler will complain otherwise)... + dff.nameTag = (DWRITE_FONT_FEATURE_TAG) DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d); dff.parameter = (UINT32) value; hr = dt->AddFontFeature(dff); if (hr != S_OK) From 45d11962b10133f9df6842b24ad7d09e05474d17 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 16:10:11 -0400 Subject: [PATCH 431/487] Turns out there was only one real runtime bug (a bad castee). It works! Now to clean up. --- windows/attrstr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index feaaa61e..79406fa6 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -382,7 +382,7 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) if (hr != S_OK) // TODO proper cleanup somehow return hr; - cea = (combinedEffectsAttr *) cea; + cea = (combinedEffectsAttr *) u; if (cea != NULL) { auto diter = effects.find(cea); if (diter != effects.end()) From 5d116d87fedfb2e8a9e05ea752673c7d22786790 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 16:14:50 -0400 Subject: [PATCH 432/487] Fixed a typo in the drawtext example and made it redraw immediately on a font change. --- examples/drawtext/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index c3b2d968..98d206f9 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -30,7 +30,7 @@ static void makeAttributedString(void) uiOpenTypeFeatures *otf; attrstr = uiNewAttributedString( - "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout obects.\n" + "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout objects.\n" "uiAttributedString lets you have a variety of attributes: "); attr = uiNewFamilyAttribute("Courier New"); @@ -129,6 +129,11 @@ static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) return 0; } +static void onFontChanged(uiFontButton *b, void *data) +{ + uiAreaQueueRedrawAll(area); +} + static int onClosing(uiWindow *w, void *data) { uiControlDestroy(uiControl(mainwin)); @@ -179,6 +184,7 @@ int main(void) uiBoxAppend(hbox, uiControl(vbox), 0); fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, onFontChanged, NULL); uiBoxAppend(vbox, uiControl(fontButton), 0); area = uiNewArea(&handler); From d358e875838ba00314e0161e235bdc210620c50c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:00:11 -0400 Subject: [PATCH 433/487] Moved the old metrics attrstr code out of the way. --- .../OLD_drawtext.c => _wip/attrstr_metrics/common_OLD_drawtext.c | 0 .../attrstr_metrics/common_OLD_uipriv_attrstr.h | 0 .../attrstr_metrics/darwin_OLD__appkit_drawtext.m | 0 .../attrstr_metrics/darwin_OLD__appkit_fontmatch.m | 0 .../attrstr_metrics/darwin_OLD__old_drawtext.m | 0 .../OLD_drawtext.m => _wip/attrstr_metrics/darwin_OLD_drawtext.m | 0 .../attrstr_metrics/unix_OLD__old_drawtext.c | 0 unix/OLD_drawtext.c => _wip/attrstr_metrics/unix_OLD_drawtext.c | 0 .../attrstr_metrics/windows_OLD__old_drawtext.cpp | 0 .../attrstr_metrics/windows_OLD_drawtext.cpp | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename common/OLD_drawtext.c => _wip/attrstr_metrics/common_OLD_drawtext.c (100%) rename common/OLD_uipriv_attrstr.h => _wip/attrstr_metrics/common_OLD_uipriv_attrstr.h (100%) rename darwin/OLD__appkit_drawtext.m => _wip/attrstr_metrics/darwin_OLD__appkit_drawtext.m (100%) rename darwin/OLD__appkit_fontmatch.m => _wip/attrstr_metrics/darwin_OLD__appkit_fontmatch.m (100%) rename darwin/OLD__old_drawtext.m => _wip/attrstr_metrics/darwin_OLD__old_drawtext.m (100%) rename darwin/OLD_drawtext.m => _wip/attrstr_metrics/darwin_OLD_drawtext.m (100%) rename unix/OLD__old_drawtext.c => _wip/attrstr_metrics/unix_OLD__old_drawtext.c (100%) rename unix/OLD_drawtext.c => _wip/attrstr_metrics/unix_OLD_drawtext.c (100%) rename windows/OLD__old_drawtext.cpp => _wip/attrstr_metrics/windows_OLD__old_drawtext.cpp (100%) rename windows/OLD_drawtext.cpp => _wip/attrstr_metrics/windows_OLD_drawtext.cpp (100%) diff --git a/common/OLD_drawtext.c b/_wip/attrstr_metrics/common_OLD_drawtext.c similarity index 100% rename from common/OLD_drawtext.c rename to _wip/attrstr_metrics/common_OLD_drawtext.c diff --git a/common/OLD_uipriv_attrstr.h b/_wip/attrstr_metrics/common_OLD_uipriv_attrstr.h similarity index 100% rename from common/OLD_uipriv_attrstr.h rename to _wip/attrstr_metrics/common_OLD_uipriv_attrstr.h diff --git a/darwin/OLD__appkit_drawtext.m b/_wip/attrstr_metrics/darwin_OLD__appkit_drawtext.m similarity index 100% rename from darwin/OLD__appkit_drawtext.m rename to _wip/attrstr_metrics/darwin_OLD__appkit_drawtext.m diff --git a/darwin/OLD__appkit_fontmatch.m b/_wip/attrstr_metrics/darwin_OLD__appkit_fontmatch.m similarity index 100% rename from darwin/OLD__appkit_fontmatch.m rename to _wip/attrstr_metrics/darwin_OLD__appkit_fontmatch.m diff --git a/darwin/OLD__old_drawtext.m b/_wip/attrstr_metrics/darwin_OLD__old_drawtext.m similarity index 100% rename from darwin/OLD__old_drawtext.m rename to _wip/attrstr_metrics/darwin_OLD__old_drawtext.m diff --git a/darwin/OLD_drawtext.m b/_wip/attrstr_metrics/darwin_OLD_drawtext.m similarity index 100% rename from darwin/OLD_drawtext.m rename to _wip/attrstr_metrics/darwin_OLD_drawtext.m diff --git a/unix/OLD__old_drawtext.c b/_wip/attrstr_metrics/unix_OLD__old_drawtext.c similarity index 100% rename from unix/OLD__old_drawtext.c rename to _wip/attrstr_metrics/unix_OLD__old_drawtext.c diff --git a/unix/OLD_drawtext.c b/_wip/attrstr_metrics/unix_OLD_drawtext.c similarity index 100% rename from unix/OLD_drawtext.c rename to _wip/attrstr_metrics/unix_OLD_drawtext.c diff --git a/windows/OLD__old_drawtext.cpp b/_wip/attrstr_metrics/windows_OLD__old_drawtext.cpp similarity index 100% rename from windows/OLD__old_drawtext.cpp rename to _wip/attrstr_metrics/windows_OLD__old_drawtext.cpp diff --git a/windows/OLD_drawtext.cpp b/_wip/attrstr_metrics/windows_OLD_drawtext.cpp similarity index 100% rename from windows/OLD_drawtext.cpp rename to _wip/attrstr_metrics/windows_OLD_drawtext.cpp From fa293717af8dec5087ad3c9fc112b2939d969011 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:05:05 -0400 Subject: [PATCH 434/487] Removed the NumLines and LineByteRange functions for now; I'll count them under extents. --- _wip/attrstr_metrics/numlinesbyterange | 60 +++++++++++++++++++ .../attrstr_metrics/old_ui_attrstr.h | 9 +++ darwin/drawtext.m | 19 ------ ui_attrstr.h | 9 --- unix/drawtext.c | 15 ----- windows/drawtext.cpp | 22 ------- 6 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 _wip/attrstr_metrics/numlinesbyterange rename old_ui_attrstr.h => _wip/attrstr_metrics/old_ui_attrstr.h (85%) diff --git a/_wip/attrstr_metrics/numlinesbyterange b/_wip/attrstr_metrics/numlinesbyterange new file mode 100644 index 00000000..46a7ba21 --- /dev/null +++ b/_wip/attrstr_metrics/numlinesbyterange @@ -0,0 +1,60 @@ +darwin +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return CFArrayGetCount([tl->forLines lines]); +} + +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + CTLineRef lr; + CFRange range; + + lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line); + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + if (tl->empty) + *end = *start; + else + *end = tl->u16tou8[range.location + range.length]; +} + + +unix +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return pango_layout_get_line_count(tl->layout); +} + +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + *start = pll->start_index; + *end = pll->start_index + pll->length; + // TODO unref pll? +} + + +windows +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ +return 0; +#if 0 +TODO + return tl->nLines; +#endif +} + +// DirectWrite doesn't provide a direct way to do this, so we have to do this manually +// TODO does that comment still apply here or to the code at the top of this file? +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ +#if 0 +TODO + *start = tl->lineInfo[line].startPos; + *start = tl->u16tou8[*start]; + *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; + *end = tl->u16tou8[*end]; +#endif +} diff --git a/old_ui_attrstr.h b/_wip/attrstr_metrics/old_ui_attrstr.h similarity index 85% rename from old_ui_attrstr.h rename to _wip/attrstr_metrics/old_ui_attrstr.h index 75df996d..ebe9308d 100644 --- a/old_ui_attrstr.h +++ b/_wip/attrstr_metrics/old_ui_attrstr.h @@ -31,6 +31,15 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); + _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); // TODO rewrite this documentation diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 5406617c..c04b402b 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -212,22 +212,3 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height [tl->frame returnWidth:width height:NULL]; [tl->forLines returnWidth:NULL height:height]; } - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ - return CFArrayGetCount([tl->forLines lines]); -} - -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ - CTLineRef lr; - CFRange range; - - lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line); - range = CTLineGetStringRange(lr); - *start = tl->u16tou8[range.location]; - if (tl->empty) - *end = *start; - else - *end = tl->u16tou8[range.location + range.length]; -} diff --git a/ui_attrstr.h b/ui_attrstr.h index 66b0fb4b..f4302cd5 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -484,15 +484,6 @@ _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, dou // function to get the actual size of the text layout. _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); -// uiDrawTextLayoutNumLines() returns the number of lines in tl. -// This number will always be greater than or equal to 1; a text -// layout with no text only has one line. -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); - -// uiDrawTextLayoutLineByteRange() returns the byte indices of the -// text that falls into the given line of tl as [start, end). -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); - // TODO metrics functions // TODO number of lines visible for clipping rect, range visible for clipping rect? diff --git a/unix/drawtext.c b/unix/drawtext.c index 5792f9e0..477e9ca3 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -79,18 +79,3 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height *width = pangoToCairo(logical.width); *height = pangoToCairo(logical.height); } - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ - return pango_layout_get_line_count(tl->layout); -} - -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ - PangoLayoutLine *pll; - - pll = pango_layout_get_line_readonly(tl->layout, line); - *start = pll->start_index; - *end = pll->start_index + pll->length; - // TODO unref pll? -} diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 56a7dfbb..85accab1 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -534,25 +534,3 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height // TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too) *height = metrics.height; } - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ -return 0; -#if 0 -TODO - return tl->nLines; -#endif -} - -// DirectWrite doesn't provide a direct way to do this, so we have to do this manually -// TODO does that comment still apply here or to the code at the top of this file? -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ -#if 0 -TODO - *start = tl->lineInfo[line].startPos; - *start = tl->u16tou8[*start]; - *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; - *end = tl->u16tou8[*end]; -#endif -} From 70321353a13ba63af3f3f546053dec679da66493 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:06:45 -0400 Subject: [PATCH 435/487] Moved a few more future files out of the way. --- checklist_attrstr => _future/unittest/checklist_attrstr | 0 .../attrstr_metrics/textDarwinEmptyLayout.diff | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename checklist_attrstr => _future/unittest/checklist_attrstr (100%) rename textDarwinEmptyLayout.diff => _wip/attrstr_metrics/textDarwinEmptyLayout.diff (100%) diff --git a/checklist_attrstr b/_future/unittest/checklist_attrstr similarity index 100% rename from checklist_attrstr rename to _future/unittest/checklist_attrstr diff --git a/textDarwinEmptyLayout.diff b/_wip/attrstr_metrics/textDarwinEmptyLayout.diff similarity index 100% rename from textDarwinEmptyLayout.diff rename to _wip/attrstr_metrics/textDarwinEmptyLayout.diff From 78e06844356ebae83de048035e98a7c36c1cd10d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:26:34 -0400 Subject: [PATCH 436/487] Cleaned up memory management in windows attrstr.cpp; also got rid of the logHRESULT(HELP) I was using in case DirectWrite doesn't return NULL+S_OK on a nonexistent drawing effect (thankfully it does). --- windows/attrstr.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 79406fa6..9ea0f755 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -240,10 +240,8 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, while (start < end) { hr = p->layout->GetDrawingEffect(start, &u, &range); if (hr != S_OK) -{logHRESULT(L"HELP", hr); - // TODO proper cleanup somehow return hr; -} cea = (combinedEffectsAttr *) u; + cea = (combinedEffectsAttr *) u; if (cea == NULL) cea = new combinedEffectsAttr(attr); else @@ -257,10 +255,11 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, if ((range.startPosition + range.length) > end) range.length = end - range.startPosition; hr = p->layout->SetDrawingEffect(cea, range); + // SetDrawingEffect will AddRef(), so Release() our copy + // (and we're abandoning early if that failed, so this will make sure things are cleaned up in that case) + cea->Release(); if (hr != S_OK) - // TODO proper cleanup somehow return hr; - // TODO figure out what and when needs to be released start += range.length; } return S_OK; @@ -377,11 +376,13 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) // go through, replacing every combinedEffectsAttr with the proper drawingEffectsAttr range.startPosition = 0; + // and in case this while loop never runs, make hr valid to start with + hr = S_OK; while (range.startPosition < p->len) { hr = p->layout->GetDrawingEffect(range.startPosition, &u, &range); if (hr != S_OK) - // TODO proper cleanup somehow - return hr; + // note that we are breaking instead of returning; this allows us to clean up on failure + break; cea = (combinedEffectsAttr *) u; if (cea != NULL) { auto diter = effects.find(cea); @@ -392,22 +393,21 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) effects.insert({cea, dea}); } hr = p->layout->SetDrawingEffect(dea, range); + // don't release dea; we need the reference that's inside the map + // (we don't take extra references on lookup, so this will be fine) if (hr != S_OK) - // TODO proper cleanup somehow - return hr; + break; } range.startPosition += range.length; } // and clean up, finally destroying the combinedEffectAttrs too -#if 0 -TODO + // we do this in the case of failure as well, to make sure everything is properly cleaned up for (auto iter = effects.begin(); iter != effects.end(); iter++) { iter->first->Release(); iter->second->Release(); } -#endif - return S_OK; + return hr; } void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundParams) From 8709838a8f409919c5008d9a0bd1b7b726f661b1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:07:40 -0400 Subject: [PATCH 437/487] Added a uiFreeFontButtonFont() function to free resources allocated by uiFontButtonFont(), implemented it on Windows, and integrated it into the drawtext example. I'm going to continue chipping away at the Windows code for a bit longer, so I'll add this to the other platforms later. --- examples/drawtext/main.c | 1 + ui_attrstr.h | 1 + windows/fontbutton.cpp | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 98d206f9..615340e7 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -106,6 +106,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) // TODO clip to margins uiDrawText(p->Context, textLayout, margins, margins); uiDrawFreeTextLayout(textLayout); + uiFreeFontButtonFont(&defaultFont); } static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) diff --git a/ui_attrstr.h b/ui_attrstr.h index f4302cd5..3486b4e9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -495,3 +495,4 @@ _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); _UI_EXTERN uiFontButton *uiNewFontButton(void); +_UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index 29de64b6..d6e5e0d8 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -121,3 +121,8 @@ uiFontButton *uiNewFontButton(void) return b; } + +void uiFreeFontButtonFont(uiFontDescriptor *desc) +{ + uiprivFree((char *) (desc->Family)); +} From 93f0eea140914fb7bc1d4458a8d7bba5bd59a6f2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:21:54 -0400 Subject: [PATCH 438/487] Likewise codified uiprivStricmp(). Honestly this will probably do for the Windows code for now... --- common/attrstr.h | 5 ----- common/uipriv.h | 3 +++ windows/text.cpp | 13 +++++++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index fb2346d1..2f4e42b4 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,11 +5,6 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) -#ifndef _WIN32 -#define uiprivStricmp(x, y) strcasecmp(x, y) -#else -#define uiprivStricmp(x, y) stricmp(x, y) -#endif // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); diff --git a/common/uipriv.h b/common/uipriv.h index 3cd23605..02676a16 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -58,6 +58,9 @@ extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); +// OS-specific text.* files +extern int uiprivStricmp(const char *a, const char *b); + #ifdef __cplusplus } #endif diff --git a/windows/text.cpp b/windows/text.cpp index af79fb80..e3a23570 100644 --- a/windows/text.cpp +++ b/windows/text.cpp @@ -105,3 +105,16 @@ void uiWindowsSetWindowText(HWND hwnd, const char *text) setWindowText(hwnd, wtext); uiFree(wtext); } + +int uiprivStricmp(const char *a, const char *b) +{ + WCHAR *wa, *wb; + int ret; + + wa = toUTF16(a); + wb = toUTF16(b); + ret = _wcsicmp(wa, wb); + uiFree(wb); + uiFree(wa); + return ret; +} From 0125e33720b9616bdd3a69da18b6b155b17ce4dd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:42:54 -0400 Subject: [PATCH 439/487] Made the likewise changes on Unix. --- unix/fontbutton.c | 6 ++++++ unix/text.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/unix/fontbutton.c b/unix/fontbutton.c index b19cd310..d239952b 100644 --- a/unix/fontbutton.c +++ b/unix/fontbutton.c @@ -67,3 +67,9 @@ uiFontButton *uiNewFontButton(void) return b; } + +void uiFreeFontButtonFont(uiFontDescriptor *desc) +{ + // TODO ensure this is synchronized with fontmatch.c + uiFreeText((char *) (desc->Family)); +} diff --git a/unix/text.c b/unix/text.c index ad92738d..9a2a9dc0 100644 --- a/unix/text.c +++ b/unix/text.c @@ -10,3 +10,8 @@ void uiFreeText(char *t) { g_free(t); } + +int uiprivStricmp(const char *a, const char *b) +{ + return strcasecmp(a, b); +} From bc895d67078e52743b706bb2932ad81d8f9f4cd8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:55:33 -0400 Subject: [PATCH 440/487] And added the necessary functions on OS X. Now to do some final cleanup before merging back in (at long last). --- darwin/fontbutton.m | 6 ++++++ darwin/text.m | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index 86f1c140..0ef57d81 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -224,3 +224,9 @@ uiFontButton *uiNewFontButton(void) return b; } + +void uiFreeFontButtonFont(uiFontDescriptor *desc) +{ + // TODO ensure this is synchronized with fontmatch.m + uiFreeText((char *) (desc->Family)); +} diff --git a/darwin/text.m b/darwin/text.m index f0d3dab6..8efd36fe 100644 --- a/darwin/text.m +++ b/darwin/text.m @@ -17,3 +17,8 @@ void uiFreeText(char *s) { free(s); } + +int uiprivStricmp(const char *a, const char *b) +{ + return strcasecmp(a, b); +} From 9aea7fa62e6d1880f4e4a80b501ac25d2a47c3c8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 11:24:09 -0400 Subject: [PATCH 441/487] Polished up the drawtext demo a bit (such as finally fixing that titlebar). Also more crash-related TODOs. --- darwin/fontmatch.m | 3 +++ examples/drawtext/main.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index f459a165..5b56f258 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -2,6 +2,9 @@ #import "uipriv_darwin.h" #import "attrstr.h" +// TODOs: +// - switching from Skia to a non-fvar-based font crashes because the CTFontDescriptorRef we get has an empty variation dictionary for some reason... + // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature // adopted by OpenType, expose variations directly. diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 615340e7..45549dff 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -88,7 +88,9 @@ static void makeAttributedString(void) attr = uiNewFeaturesAttribute(otf); appendWithAttribute("afford", attr, NULL); uiFreeOpenTypeFeatures(otf); - uiAttributedStringAppendUnattributed(attrstr, ")."); + uiAttributedStringAppendUnattributed(attrstr, ").\n"); + + uiAttributedStringAppendUnattributed(attrstr, "Use the controls opposite to the text to control properties of the text."); } static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) @@ -172,7 +174,7 @@ int main(void) makeAttributedString(); - mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); + mainwin = uiNewWindow("libui Text-Drawing Example", 640, 480, 1); uiWindowSetMargined(mainwin, 1); uiWindowOnClosing(mainwin, onClosing, NULL); From 8944a3fc5528445b9027b1294b6c86bae03eeb89 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:11:03 -0400 Subject: [PATCH 442/487] Finally documented the remaining functions in ui_attrstr.h. --- _future/unittest/checklist_attrstr | 1 + ui_attrstr.h | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/_future/unittest/checklist_attrstr b/_future/unittest/checklist_attrstr index 47117def..7ba0f244 100644 --- a/_future/unittest/checklist_attrstr +++ b/_future/unittest/checklist_attrstr @@ -22,3 +22,4 @@ should uiNewFamilyAttribute() accept NULL it is an error in ForEach too invalid values for uiDrawTextAlign empty text layouts have one line +TODO figure out what to do if any field (particularly the font family name) in uiFontDescriptor is unset diff --git a/ui_attrstr.h b/ui_attrstr.h index 3486b4e9..66eaf1ce 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -488,11 +488,20 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou // TODO number of lines visible for clipping rect, range visible for clipping rect? +// uiFontButton is a button that allows users to choose a font when they click on it. typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) +// uiFontButtonFont() returns the font currently selected in the uiFontButton in desc. +// uiFontButtonFont() allocates resources in desc; when you are done with the font, call uiFreeFontButtonFont() to release them. +// uiFontButtonFont() does not allocate desc itself; you must do so. // TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); // TOOD SetFont, mechanics +// uiFontButtonOnChanged() sets the function that is called when the font in the uiFontButton is changed. _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +// uiNewFontButton() creates a new uiFontButton. The default font selected into the uiFontButton is OS-defined. _UI_EXTERN uiFontButton *uiNewFontButton(void); +// uiFreeFontButtonFont() frees resources allocated in desc by uiFontButtonFont(). +// After calling uiFreeFontButtonFont(), the contents of desc should be assumed to be undefined (though since you allocate desc itself, you can safely reuse desc for other font descriptors). +// Calling uiFreeFontButtonFont() on a uiFontDescriptor not returned by uiFontButtonFont() results in undefined behavior. _UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); From a0d2d6a1f87a536580cc2a6b8915c90383d7f22a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:30:50 -0400 Subject: [PATCH 443/487] Added alignment to the drawtext example. --- examples/drawtext/main.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 45549dff..3f6e403e 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -7,6 +7,7 @@ uiWindow *mainwin; uiArea *area; uiAreaHandler handler; uiFontButton *fontButton; +uiCombobox *alignment; uiAttributedString *attrstr; @@ -103,7 +104,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) uiFontButtonFont(fontButton, &defaultFont); params.DefaultFont = &defaultFont; params.Width = p->AreaWidth - (2 * margins); - params.Align = uiDrawTextAlignLeft; + params.Align = (uiDrawTextAlign) uiComboboxSelected(alignment); textLayout = uiDrawNewTextLayout(¶ms); // TODO clip to margins uiDrawText(p->Context, textLayout, margins, margins); @@ -137,6 +138,11 @@ static void onFontChanged(uiFontButton *b, void *data) uiAreaQueueRedrawAll(area); } +static void onComboboxSelected(uiCombobox *b, void *data) +{ + uiAreaQueueRedrawAll(area); +} + static int onClosing(uiWindow *w, void *data) { uiControlDestroy(uiControl(mainwin)); @@ -155,6 +161,7 @@ int main(void) uiInitOptions o; const char *err; uiBox *hbox, *vbox; + uiForm *form; handler.Draw = handlerDraw; handler.MouseEvent = handlerMouseEvent; @@ -190,6 +197,20 @@ int main(void) uiFontButtonOnChanged(fontButton, onFontChanged, NULL); uiBoxAppend(vbox, uiControl(fontButton), 0); + form = uiNewForm(); + uiFormSetPadded(form, 1); + // TODO on OS X if this is set to 1 then the window can't resize; does the form not have the concept of stretchy trailing space? + uiBoxAppend(vbox, uiControl(form), 0); + + alignment = uiNewCombobox(); + // note that the items match with the values of the uiDrawTextAlign values + uiComboboxAppend(alignment, "Left"); + uiComboboxAppend(alignment, "Center"); + uiComboboxAppend(alignment, "Right"); + uiComboboxSetSelected(alignment, 0); // start with left alignment + uiComboboxOnSelected(alignment, onComboboxSelected, NULL); + uiFormAppend(form, "Alignment", uiControl(alignment), 0); + area = uiNewArea(&handler); uiBoxAppend(hbox, uiControl(area), 1); From d788d86239a8b09d1040ccaa831ead03000b4166 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:39:44 -0400 Subject: [PATCH 444/487] Removed the margins from the drawtext example. It looks better this way. --- examples/drawtext/main.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 3f6e403e..d94d2572 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -11,8 +11,6 @@ uiCombobox *alignment; uiAttributedString *attrstr; -#define margins 20 - static void appendWithAttribute(const char *what, uiAttribute *attr, uiAttribute *attr2) { size_t start, end; @@ -103,11 +101,10 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) params.String = attrstr; uiFontButtonFont(fontButton, &defaultFont); params.DefaultFont = &defaultFont; - params.Width = p->AreaWidth - (2 * margins); + params.Width = p->AreaWidth; params.Align = (uiDrawTextAlign) uiComboboxSelected(alignment); textLayout = uiDrawNewTextLayout(¶ms); - // TODO clip to margins - uiDrawText(p->Context, textLayout, margins, margins); + uiDrawText(p->Context, textLayout, 0, 0); uiDrawFreeTextLayout(textLayout); uiFreeFontButtonFont(&defaultFont); } From df03c09a9cab88f253b3bc4dccf8a6ceb879cf02 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:40:45 -0400 Subject: [PATCH 445/487] More TODOs. --- darwin/fontmatch.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 5b56f258..ea075e53 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -4,6 +4,7 @@ // TODOs: // - switching from Skia to a non-fvar-based font crashes because the CTFontDescriptorRef we get has an empty variation dictionary for some reason... +// - Futura causes the Courier New in the drawtext example to be bold for some reason... // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature From c402dcac30eb8d8d11453b2601955796fc5fde5d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:49:44 -0400 Subject: [PATCH 446/487] And finally got rid of ui_attrstr.h (it's now all in ui.h) and updated the README. Time to FINALLY merge this back in! --- README.md | 6 +- ui.h | 509 ++++++++++++++++++++++++++++++++++++++++++++++++++- ui_attrstr.h | 507 -------------------------------------------------- 3 files changed, 510 insertions(+), 512 deletions(-) delete mode 100644 ui_attrstr.h diff --git a/README.md b/README.md index a1fd57ad..41edf3c1 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ This README is being written.
## Announcements -* **TODO** - * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](TODO). There are also two new examples for this new api: `drawtext` (which shows the whole API at a glance) and `opentype` (which focuses on OpenType features). - * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. +* **18 March 2018** + * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later. + * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. They are still WIP. * **17 February 2018** * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). diff --git a/ui.h b/ui.h index 0f7dce64..e3f0c908 100644 --- a/ui.h +++ b/ui.h @@ -486,8 +486,513 @@ _UI_EXTERN void uiDrawClip(uiDrawContext *c, uiDrawPath *path); _UI_EXTERN void uiDrawSave(uiDrawContext *c); _UI_EXTERN void uiDrawRestore(uiDrawContext *c); -// TODO merge back in -#include "ui_attrstr.h" +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; + +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); + +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, +}; + +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); + +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); + +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); + +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); + +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); + +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, +}; + +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, +}; + +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office +}; + +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// If a font does not support a certain feature, that feature will be +// ignored. (TODO verify this on all OSs) +// +// See the OpenType specification at +// https://www.microsoft.com/typography/otspec/featuretags.htm +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; + +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); + +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. +// +// Attributes are applied to runs of Unicode codepoints in the string. +// Zero-length runs are elided. Consecutive runs that have the same +// attribute type and value are merged. Each attribute is independent +// of each other attribute; overlapping attributes of different types +// do not split each other apart, but different values of the same +// attribute type do. +// +// The empty string can also be represented by uiAttributedString, +// but because of the no-zero-length-attribute rule, it will not have +// attributes. +// +// A uiAttributedString takes ownership of all attributes given to +// it, as it may need to duplicate or delete uiAttribute objects at +// any time. By extension, when you free a uiAttributedString, +// all uiAttributes within will also be freed. Each method will +// describe its own rules in more details. +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// TODO does uiAttributedString itself need this +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. +typedef struct uiAttributedString uiAttributedString; + +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +// It will also free all uiAttributes within. +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with their text. +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the byte range [start, end). +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + +// uiAttributedStringSetAttribute() sets a in the byte range [start, end) +// of s. Any existing attributes in that byte range of the same type are +// removed. s takes ownership of a; you should not use it after +// uiAttributedStringSetAttribute() returns. +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); + +// uiAttributedStringForEachAttribute() enumerates all the +// uiAttributes in s. It is an error to modify s in f. Within f, s still +// owns the attribute; you can neither free it nor save it for later +// use. +// TODO reword the above for consistency (TODO and find out what I meant by that) +// TODO define an enumeration order (or mark it as undefined); also define how consecutive runs of identical attributes are handled here and sync with the definition of uiAttributedString itself +_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +// uiFontDescriptor provides a complete description of a font where +// one is needed. Currently, this means as the default font of a +// uiDrawTextLayout and as the data returned by uiFontButton. +// All the members operate like the respective uiAttributes. +typedef struct uiFontDescriptor uiFontDescriptor; + +struct uiFontDescriptor { + // TODO const-correct this or figure out how to deal with this when getting a value + char *Family; + double Size; + uiTextWeight Weight; + uiTextItalic Italic; + uiTextStretch Stretch; +}; + +// uiDrawTextLayout is a concrete representation of a +// uiAttributedString that can be displayed in a uiDrawContext. +// It includes information important for the drawing of a block of +// text, including the bounding box to wrap the text within, the +// alignment of lines of text within that box, areas to mark as +// being selected, and other things. +// +// Unlike uiAttributedString, the content of a uiDrawTextLayout is +// immutable once it has been created. +// +// TODO talk about OS-specific differences with text drawing that libui can't account for... +typedef struct uiDrawTextLayout uiDrawTextLayout; + +// uiDrawTextAlign specifies the alignment of lines of text in a +// uiDrawTextLayout. +// TODO should this really have Draw in the name? +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, +}; + +// uiDrawTextLayoutParams describes a uiDrawTextLayout. +// DefaultFont is used to render any text that is not attributed +// sufficiently in String. Width determines the width of the bounding +// box of the text; the height is determined automatically. +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + +// TODO const-correct this somehow +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiFontDescriptor *DefaultFont; + double Width; + uiDrawTextAlign Align; +}; + +// @role uiDrawTextLayout constructor +// uiDrawNewTextLayout() creates a new uiDrawTextLayout from +// the given parameters. +// +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); + +// @role uiDrawFreeTextLayout destructor +// uiDrawFreeTextLayout() frees tl. The underlying +// uiAttributedString is not freed. +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// TODO metrics functions + +// TODO number of lines visible for clipping rect, range visible for clipping rect? + +// uiFontButton is a button that allows users to choose a font when they click on it. +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// uiFontButtonFont() returns the font currently selected in the uiFontButton in desc. +// uiFontButtonFont() allocates resources in desc; when you are done with the font, call uiFreeFontButtonFont() to release them. +// uiFontButtonFont() does not allocate desc itself; you must do so. +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); +// TOOD SetFont, mechanics +// uiFontButtonOnChanged() sets the function that is called when the font in the uiFontButton is changed. +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +// uiNewFontButton() creates a new uiFontButton. The default font selected into the uiFontButton is OS-defined. +_UI_EXTERN uiFontButton *uiNewFontButton(void); +// uiFreeFontButtonFont() frees resources allocated in desc by uiFontButtonFont(). +// After calling uiFreeFontButtonFont(), the contents of desc should be assumed to be undefined (though since you allocate desc itself, you can safely reuse desc for other font descriptors). +// Calling uiFreeFontButtonFont() on a uiFontDescriptor not returned by uiFontButtonFont() results in undefined behavior. +_UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); _UI_ENUM(uiModifiers) { uiModifierCtrl = 1 << 0, diff --git a/ui_attrstr.h b/ui_attrstr.h deleted file mode 100644 index 66eaf1ce..00000000 --- a/ui_attrstr.h +++ /dev/null @@ -1,507 +0,0 @@ -// uiAttribute stores information about an attribute in a -// uiAttributedString. -// -// You do not create uiAttributes directly; instead, you create a -// uiAttribute of a given type using the specialized constructor -// functions. For every Unicode codepoint in the uiAttributedString, -// at most one value of each attribute type can be applied. -// -// uiAttributes are immutable and the uiAttributedString takes -// ownership of the uiAttribute object once assigned, copying its -// contents as necessary. -typedef struct uiAttribute uiAttribute; - -// uiFreeAttribute() frees a uiAttribute. You generally do not need to -// call this yourself, as uiAttributedString does this for you. In fact, -// it is an error to call this function on a uiAttribute that has been -// given to a uiAttributedString. You can call this, however, if you -// created a uiAttribute that you aren't going to use later. -_UI_EXTERN void uiFreeAttribute(uiAttribute *a); - -// uiAttributeType holds the possible uiAttribute types that may be -// returned by uiAttributeGetType(). Refer to the documentation for -// each type's constructor function for details on each type. -_UI_ENUM(uiAttributeType) { - uiAttributeTypeFamily, - uiAttributeTypeSize, - uiAttributeTypeWeight, - uiAttributeTypeItalic, - uiAttributeTypeStretch, - uiAttributeTypeColor, - uiAttributeTypeBackground, - uiAttributeTypeUnderline, - uiAttributeTypeUnderlineColor, - uiAttributeTypeFeatures, -}; - -// uiAttributeGetType() returns the type of a. -// TODO I don't like this name -_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); - -// uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. family is copied; you do not -// need to keep it alive after uiNewFamilyAttribute() returns. Font -// family names are case-insensitive. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); - -// uiAttributeFamily() returns the font family stored in a. The -// returned string is owned by a. It is an error to call this on a -// uiAttribute that does not hold a font family. -_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); - -// uiNewSizeAttribute() creates a new uiAttribute that changes the -// size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); - -// uiAttributeSize() returns the font size stored in a. It is an error to -// call this on a uiAttribute that does not hold a font size. -_UI_EXTERN double uiAttributeSize(const uiAttribute *a); - -// uiTextWeight represents possible text weights. These roughly -// map to the OSx2 text weight field of TrueType and OpenType -// fonts, or to CSS weight numbers. The named constants are -// nominal values; the actual values may vary by font and by OS, -// though this isn't particularly likely. Any value between -// uiTextWeightMinimum and uiDrawTextWeightMaximum, -// inclusive, is allowed. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" weights be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Black. libui does not do this, even on Windows (because the -// DirectWrite API libui uses on Windows does not do this); to -// specify Arial Black, use family Arial and weight uiTextWeightBlack. -_UI_ENUM(uiTextWeight) { - uiTextWeightMinimum = 0, - uiTextWeightThin = 100, - uiTextWeightUltraLight = 200, - uiTextWeightLight = 300, - uiTextWeightBook = 350, - uiTextWeightNormal = 400, - uiTextWeightMedium = 500, - uiTextWeightSemiBold = 600, - uiTextWeightBold = 700, - uiTextWeightUltraBold = 800, - uiTextWeightHeavy = 900, - uiTextWeightUltraHeavy = 950, - uiTextWeightMaximum = 1000, -}; - -// uiNewWeightAttribute() creates a new uiAttribute that changes the -// weight of the text it is applied to. It is an error to specify a weight -// outside the range [uiTextWeightMinimum, -// uiTextWeightMaximum]. -_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); - -// uiAttributeWeight() returns the font weight stored in a. It is an error -// to call this on a uiAttribute that does not hold a font weight. -_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); - -// uiTextItalic represents possible italic modes for a font. Italic -// represents "true" italics where the slanted glyphs have custom -// shapes, whereas oblique represents italics that are merely slanted -// versions of the normal glyphs. Most fonts usually have one or the -// other. -_UI_ENUM(uiTextItalic) { - uiTextItalicNormal, - uiTextItalicOblique, - uiTextItalicItalic, -}; - -// uiNewItalicAttribute() creates a new uiAttribute that changes the -// italic mode of the text it is applied to. It is an error to specify an -// italic mode not specified in uiTextItalic. -_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); - -// uiAttributeItalic() returns the font italic mode stored in a. It is an -// error to call this on a uiAttribute that does not hold a font italic -// mode. -_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); - -// uiTextStretch represents possible stretches (also called "widths") -// of a font. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" stretches be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Condensed. libui does not do this, even on Windows (because -// the DirectWrite API libui uses on Windows does not do this); to -// specify Arial Condensed, use family Arial and stretch -// uiTextStretchCondensed. -_UI_ENUM(uiTextStretch) { - uiTextStretchUltraCondensed, - uiTextStretchExtraCondensed, - uiTextStretchCondensed, - uiTextStretchSemiCondensed, - uiTextStretchNormal, - uiTextStretchSemiExpanded, - uiTextStretchExpanded, - uiTextStretchExtraExpanded, - uiTextStretchUltraExpanded, -}; - -// uiNewStretchAttribute() creates a new uiAttribute that changes the -// stretch of the text it is applied to. It is an error to specify a strech -// not specified in uiTextStretch. -_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); - -// uiAttributeStretch() returns the font stretch stored in a. It is an -// error to call this on a uiAttribute that does not hold a font stretch. -_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); - -// uiNewColorAttribute() creates a new uiAttribute that changes the -// color of the text it is applied to. It is an error to specify an invalid -// color. -_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); - -// uiAttributeColor() returns the text color stored in a. It is an -// error to call this on a uiAttribute that does not hold a text color. -_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); - -// uiNewBackgroundAttribute() creates a new uiAttribute that -// changes the background color of the text it is applied to. It is an -// error to specify an invalid color. -_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); - -// TODO reuse uiAttributeColor() for background colors, or make a new function... - -// uiUnderline specifies a type of underline to use on text. -_UI_ENUM(uiUnderline) { - uiUnderlineNone, - uiUnderlineSingle, - uiUnderlineDouble, - uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers -}; - -// uiNewUnderlineAttribute() creates a new uiAttribute that changes -// the type of underline on the text it is applied to. It is an error to -// specify an underline type not specified in uiUnderline. -_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); - -// uiAttributeUnderline() returns the underline type stored in a. It is -// an error to call this on a uiAttribute that does not hold an underline -// style. -_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); - -// uiUnderlineColor specifies the color of any underline on the text it -// is applied to, regardless of the type of underline. In addition to -// being able to specify a custom color, you can explicitly specify -// platform-specific colors for suggestion underlines; to use them -// correctly, pair them with uiUnderlineSuggestion (though they can -// be used on other types of underline as well). -// -// If an underline type is applied but no underline color is -// specified, the text color is used instead. If an underline color -// is specified without an underline type, the underline color -// attribute is ignored, but not removed from the uiAttributedString. -_UI_ENUM(uiUnderlineColor) { - uiUnderlineColorCustom, - uiUnderlineColorSpelling, - uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office -}; - -// uiNewUnderlineColorAttribute() creates a new uiAttribute that -// changes the color of the underline on the text it is applied to. -// It is an error to specify an underline color not specified in -// uiUnderlineColor. -// -// If the specified color type is uiUnderlineColorCustom, it is an -// error to specify an invalid color value. Otherwise, the color values -// are ignored and should be specified as zero. -_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); - -// uiAttributeUnderlineColor() returns the underline color stored in -// a. It is an error to call this on a uiAttribute that does not hold an -// underline color. -_UI_EXTERN void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); - -// uiOpenTypeFeatures represents a set of OpenType feature -// tag-value pairs, for applying OpenType features to text. -// OpenType feature tags are four-character codes defined by -// OpenType that cover things from design features like small -// caps and swashes to language-specific glyph shapes and -// beyond. Each tag may only appear once in any given -// uiOpenTypeFeatures instance. Each value is a 32-bit integer, -// often used as a Boolean flag, but sometimes as an index to choose -// a glyph shape to use. -// -// If a font does not support a certain feature, that feature will be -// ignored. (TODO verify this on all OSs) -// -// See the OpenType specification at -// https://www.microsoft.com/typography/otspec/featuretags.htm -// for the complete list of available features, information on specific -// features, and how to use them. -// TODO invalid features -typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; - -// uiOpenTypeFeaturesForEachFunc is the type of the function -// invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); - -// @role uiOpenTypeFeatures constructor -// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures -// instance, with no tags yet added. -_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); - -// @role uiOpenTypeFeatures destructor -// uiFreeOpenTypeFeatures() frees otf. -_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesAdd() adds the given feature tag and value -// to otf. The feature tag is specified by a, b, c, and d. If there is -// already a value associated with the specified tag in otf, the old -// value is removed. -_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); - -// uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. If the tag is not present in otf, -// uiOpenTypeFeaturesRemove() does nothing. -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); - -// uiOpenTypeFeaturesGet() determines whether the given feature -// tag is present in otf. If it is, *value is set to the tag's value and -// nonzero is returned. Otherwise, zero is returned. -// -// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't -// changed. This is important: if a feature is not present in a -// uiOpenTypeFeatures, the feature is NOT treated as if its -// value was zero anyway. Script-specific font shaping rules and -// font-specific feature settings may use a different default value -// for a feature. You should likewise not treat a missing feature as -// having a value of zero either. Instead, a missing feature should -// be treated as having some unspecified default value. -_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); - -// uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. You cannot -// modify otf while uiOpenTypeFeaturesForEach() is running. -_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); - -// uiNewFeaturesAttribute() creates a new uiAttribute that changes -// the font family of the text it is applied to. otf is copied; you may -// free it after uiNewFeaturesAttribute() returns. -_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); - -// uiAttributeFeatures() returns the OpenType features stored in a. -// The returned uiOpenTypeFeatures object is owned by a. It is an -// error to call this on a uiAttribute that does not hold OpenType -// features. -_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); - -// uiAttributedString represents a string of UTF-8 text that can -// optionally be embellished with formatting attributes. libui -// provides the list of formatting attributes, which cover common -// formatting traits like boldface and color as well as advanced -// typographical features provided by OpenType like superscripts -// and small caps. These attributes can be combined in a variety of -// ways. -// -// Attributes are applied to runs of Unicode codepoints in the string. -// Zero-length runs are elided. Consecutive runs that have the same -// attribute type and value are merged. Each attribute is independent -// of each other attribute; overlapping attributes of different types -// do not split each other apart, but different values of the same -// attribute type do. -// -// The empty string can also be represented by uiAttributedString, -// but because of the no-zero-length-attribute rule, it will not have -// attributes. -// -// A uiAttributedString takes ownership of all attributes given to -// it, as it may need to duplicate or delete uiAttribute objects at -// any time. By extension, when you free a uiAttributedString, -// all uiAttributes within will also be freed. Each method will -// describe its own rules in more details. -// -// In addition, uiAttributedString provides facilities for moving -// between grapheme clusters, which represent a character -// from the point of view of the end user. The cursor of a text editor -// is always placed on a grapheme boundary, so you can use these -// features to move the cursor left or right by one "character". -// TODO does uiAttributedString itself need this -// -// uiAttributedString does not provide enough information to be able -// to draw itself onto a uiDrawContext or respond to user actions. -// In order to do that, you'll need to use a uiDrawTextLayout, which -// is built from the combination of a uiAttributedString and a set of -// layout-specific properties. -typedef struct uiAttributedString uiAttributedString; - -// uiAttributedStringForEachAttributeFunc is the type of the function -// invoked by uiAttributedStringForEachAttribute() for every -// attribute in s. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); - -// @role uiAttributedString constructor -// uiNewAttributedString() creates a new uiAttributedString from -// initialString. The string will be entirely unattributed. -_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); - -// @role uiAttributedString destructor -// uiFreeAttributedString() destroys the uiAttributedString s. -// It will also free all uiAttributes within. -_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); - -// uiAttributedStringString() returns the textual content of s as a -// '\0'-terminated UTF-8 string. The returned pointer is valid until -// the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); - -// uiAttributedStringLength() returns the number of UTF-8 bytes in -// the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to the end of s. The new substring will be -// unattributed. -_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); - -// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated -// UTF-8 string str to s at the byte position specified by at. The new -// substring will be unattributed; existing attributes will be moved -// along with their text. -_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); - -// TODO add the Append and InsertAtExtendingAttributes functions -// TODO and add functions that take a string + length - -// uiAttributedStringDelete() deletes the characters and attributes of -// s in the byte range [start, end). -_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); - -// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - -// uiAttributedStringSetAttribute() sets a in the byte range [start, end) -// of s. Any existing attributes in that byte range of the same type are -// removed. s takes ownership of a; you should not use it after -// uiAttributedStringSetAttribute() returns. -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); - -// uiAttributedStringForEachAttribute() enumerates all the -// uiAttributes in s. It is an error to modify s in f. Within f, s still -// owns the attribute; you can neither free it nor save it for later -// use. -// TODO reword the above for consistency (TODO and find out what I meant by that) -// TODO define an enumeration order (or mark it as undefined); also define how consecutive runs of identical attributes are handled here and sync with the definition of uiAttributedString itself -_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); - -// uiFontDescriptor provides a complete description of a font where -// one is needed. Currently, this means as the default font of a -// uiDrawTextLayout and as the data returned by uiFontButton. -// All the members operate like the respective uiAttributes. -typedef struct uiFontDescriptor uiFontDescriptor; - -struct uiFontDescriptor { - // TODO const-correct this or figure out how to deal with this when getting a value - char *Family; - double Size; - uiTextWeight Weight; - uiTextItalic Italic; - uiTextStretch Stretch; -}; - -// uiDrawTextLayout is a concrete representation of a -// uiAttributedString that can be displayed in a uiDrawContext. -// It includes information important for the drawing of a block of -// text, including the bounding box to wrap the text within, the -// alignment of lines of text within that box, areas to mark as -// being selected, and other things. -// -// Unlike uiAttributedString, the content of a uiDrawTextLayout is -// immutable once it has been created. -// -// TODO talk about OS-specific differences with text drawing that libui can't account for... -typedef struct uiDrawTextLayout uiDrawTextLayout; - -// uiDrawTextAlign specifies the alignment of lines of text in a -// uiDrawTextLayout. -// TODO should this really have Draw in the name? -_UI_ENUM(uiDrawTextAlign) { - uiDrawTextAlignLeft, - uiDrawTextAlignCenter, - uiDrawTextAlignRight, -}; - -// uiDrawTextLayoutParams describes a uiDrawTextLayout. -// DefaultFont is used to render any text that is not attributed -// sufficiently in String. Width determines the width of the bounding -// box of the text; the height is determined automatically. -typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; - -// TODO const-correct this somehow -struct uiDrawTextLayoutParams { - uiAttributedString *String; - uiFontDescriptor *DefaultFont; - double Width; - uiDrawTextAlign Align; -}; - -// @role uiDrawTextLayout constructor -// uiDrawNewTextLayout() creates a new uiDrawTextLayout from -// the given parameters. -// -// TODO -// - allow creating a layout out of a substring -// - allow marking compositon strings -// - allow marking selections, even after creation -// - add the following functions: -// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) -// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) -// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) -// - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); - -// @role uiDrawFreeTextLayout destructor -// uiDrawFreeTextLayout() frees tl. The underlying -// uiAttributedString is not freed. -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); - -// uiDrawText() draws tl in c with the top-left point of tl at (x, y). -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); - -// uiDrawTextLayoutExtents() returns the width and height of tl -// in width and height. The returned width may be smaller than -// the width passed into uiDrawNewTextLayout() depending on -// how the text in tl is wrapped. Therefore, you can use this -// function to get the actual size of the text layout. -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); - -// TODO metrics functions - -// TODO number of lines visible for clipping rect, range visible for clipping rect? - -// uiFontButton is a button that allows users to choose a font when they click on it. -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// uiFontButtonFont() returns the font currently selected in the uiFontButton in desc. -// uiFontButtonFont() allocates resources in desc; when you are done with the font, call uiFreeFontButtonFont() to release them. -// uiFontButtonFont() does not allocate desc itself; you must do so. -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? -_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); -// TOOD SetFont, mechanics -// uiFontButtonOnChanged() sets the function that is called when the font in the uiFontButton is changed. -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -// uiNewFontButton() creates a new uiFontButton. The default font selected into the uiFontButton is OS-defined. -_UI_EXTERN uiFontButton *uiNewFontButton(void); -// uiFreeFontButtonFont() frees resources allocated in desc by uiFontButtonFont(). -// After calling uiFreeFontButtonFont(), the contents of desc should be assumed to be undefined (though since you allocate desc itself, you can safely reuse desc for other font descriptors). -// Calling uiFreeFontButtonFont() on a uiFontDescriptor not returned by uiFontButtonFont() results in undefined behavior. -_UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); From 8d6e41e1998e500bc0f581abd66648e5028de7c5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:54:55 -0400 Subject: [PATCH 447/487] Oops, missed a spot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 41edf3c1..472781db 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This README is being written.
* **18 March 2018** * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later. + * libui also now uses my [utf library](https://github.com/andlabs/utf) for UTF-8 and UTF-16 processing, to allow consistent behavior across platforms. This usage is not completely propagated throughout libui, but the Windows port uses it in most places now, and eventually this will become what libui will use throughout. * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. They are still WIP. * **17 February 2018** From 3e76d799b18933226b511a8941fb9589cb873c88 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Mar 2018 01:58:34 -0400 Subject: [PATCH 448/487] Fixed leftovers from utflib-and-attrstr that broke builds of things I didn't fully update yet, since people want to build them anyway (for testing in a CI environment, I suppose; a real unit test suite would be better for this, though, which is one of the reasons for the _future/unittest stuff...) Updates #302. --- examples/CMakeLists.txt | 3 +-- test/CMakeLists.txt | 6 +++--- test/main.c | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cdedb039..75b7f9d1 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -41,5 +41,4 @@ add_custom_target(examples controlgallery histogram cpp-multithread - drawtext - opentype) + drawtext) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e4924bb3..c2f9c1a9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,9 +18,9 @@ _add_exec(tester page7a.c page7b.c page7c.c - page8.c - page9.c - page10.c +# page8.c +# page9.c +# page10.c page11.c page12.c page13.c diff --git a/test/main.c b/test/main.c index 18774dcd..aa38784f 100644 --- a/test/main.c +++ b/test/main.c @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) page7 = makePage7(); uiTabAppend(innerTab, "Page 7", uiControl(page7)); - page8 = makePage8(); +/* page8 = makePage8(); uiTabAppend(innerTab, "Page 8", uiControl(page8)); page9 = makePage9(); @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) page10 = makePage10(); uiTabAppend(innerTab, "Page 10", uiControl(page10)); - +*/ innerTab = newTab(); uiTabAppend(outerTab, "Pages 11-15", uiControl(innerTab)); From 242620ff0f111898e61f84b1dc758cc0be67c28d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Mar 2018 02:10:48 -0400 Subject: [PATCH 449/487] Fixing deployment target issues on OS X broke cpp-multithread due to deployment target libc++ issues. Fixed. Fixes #302. --- examples/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 75b7f9d1..61ed7b82 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -30,6 +30,12 @@ _add_example(cpp-multithread if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() +if(APPLE) + # since we use a deployment target of 10.8, the non-C++11-compliant libstdc++ is chosen by default; we need C++11 + # see issue #302 for more details + target_compile_options(cpp-multithread PRIVATE --stdlib=libc++) + target_link_libraries(cpp-multithread --stdlib=libc++) +endif() _add_example(drawtext drawtext/main.c From 93789560ceae9fcaa44635d10a6167184b350f76 Mon Sep 17 00:00:00 2001 From: marcotrosi Date: Thu, 22 Mar 2018 18:37:07 +0100 Subject: [PATCH 450/487] added link to Lua binding library lui by Gunnar Z. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa62632e..ad56dffc 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ Harbour | [HBUI](https://github.com/RJopek/HBUI) Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) -Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) +Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html) Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) PHP | [ui](https://github.com/krakjoe/ui) From 51f72abba84e3941deba1c8f699046a48d1f7fb9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Mar 2018 08:25:41 -0400 Subject: [PATCH 451/487] Updated the list of bindings, adding new ones and removing some deleted ones. Fixed #244. FIxed #217. Updates #206. --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ad56dffc..e94195b4 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ Other people have made bindings to other languages: Language | Bindings --- | --- +C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike) C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) @@ -161,16 +162,17 @@ Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Harbour | [HBUI](https://github.com/RJopek/HBUI) -Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui) -JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) +Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui) +JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html) Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) PHP | [ui](https://github.com/krakjoe/ui) -Python | [pylibui](https://github.com/joaoventura/pylibui) +Python | [pylibui](https://github.com/joaoventura/pylibui), [pylibui-cffi](https://github.com/Yardanico/pylibui-cffi) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) Rust | [libui-rs](https://github.com/pcwalton/libui-rs) +Scala | [scalaui](https://github.com/lolgab/scalaui) Swift | [libui-swift](https://github.com/sclukey/libui-swift) ## Frequently Asked Questions From 84ff1890c5d9da5db181eeba33a8c0ccccd5cce3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 24 Mar 2018 21:16:58 -0400 Subject: [PATCH 452/487] More notes. --- _notes/textSelections | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 _notes/textSelections diff --git a/_notes/textSelections b/_notes/textSelections new file mode 100644 index 00000000..a1bccba6 --- /dev/null +++ b/_notes/textSelections @@ -0,0 +1,3 @@ +http://www.catch22.net/tuts/transparent-text +https://msdn.microsoft.com/en-us/library/windows/desktop/ms724371(v=vs.85).aspx + TODO which color did I want from this list From a7c58c2c2effb6f38255af83fe211f26d9c16a8e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Mar 2018 02:01:37 -0400 Subject: [PATCH 453/487] More notes. --- _notes/misc | 100 ++++++++++++++++++++++++++++++++++++++++++ _notes/windowsHighDPI | 2 + 2 files changed, 102 insertions(+) diff --git a/_notes/misc b/_notes/misc index 72381721..714a401e 100644 --- a/_notes/misc +++ b/_notes/misc @@ -1,2 +1,102 @@ windows data types, "open specifications" on msdn https://msdn.microsoft.com/en-us/library/cc230321.aspx (I usually use https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx for windows data types) + +windows platform update for windows 7 +https://msdn.microsoft.com/en-us/library/windows/desktop/jj863687(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/hh802478(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/hh802480(v=vs.85).aspx + +DWM, header bars, toolbars +https://stackoverflow.com/questions/41106347/why-is-my-dwmextendframeintoclientaread-window-not-drawing-the-dwm-borders/41125616#41125616 +https://developer.gnome.org/hig/stable/header-bars.html.en +https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/WindowTitleBarToolbar.html#//apple_ref/doc/uid/20000957-CH39-SW1 +https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/ControlsAll.html#//apple_ref/doc/uid/20000957-CH46-SW2 +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787329(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787334(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787337(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/cc835034(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787345(v=vs.85).aspx + +rendering +https://www.youtube.com/watch?v=UUfXWzp0-DU + +text input on windows +https://blogs.msdn.microsoft.com/oldnewthing/20121025-00/?p=6253 +https://www.google.com/search?q=translatemessage+site%3Ahttp%3A%2F%2Farchives.miloush.net%2Fmichkap%2Farchive%2F&ie=utf-8&oe=utf-8 +http://archives.miloush.net/michkap/archive/2008/04/22/8415843.html +http://archives.miloush.net/michkap/archive/2007/03/25/1948887.html +http://archives.miloush.net/michkap/archive/2006/09/10/748775.html +http://archives.miloush.net/michkap/archive/2004/11/27/270931.html +https://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either +http://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either?noredirect=1#comment70107257_41334851 + +text layouts +https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc +https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd368205(v=vs.85).aspx +https://developer.gnome.org/pango/1.30/pango-Layout-Objects.html#pango-layout-set-font-description +https://developer.gnome.org/pango/1.30/pango-Fonts.html#PangoWeight-enum +https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c (code for small caps) +https://bugzilla.gnome.org/show_bug.cgi?id=766148 +https://developer.apple.com/documentation/coretext/ctline?preferredLanguage=occ +https://developer.apple.com/reference/coretext/1508876-ctlinegetstringindexforposition?language=objc +https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005533-CH1-SW1 +https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc +https://developer.apple.com/reference/coretext/2168885-ctline?language=objc +https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html +http://asciiwwdc.com/2013/sessions/205 +https://developer.apple.com/reference/coretext/1511332-ctlinegetboundswithoptions?language=objc +https://stackoverflow.com/questions/19709751/ctlinegetboundswithoptions-what-does-the-returned-frame-origin-y-value-mean?rq=1 +https://imgur.com/a/lqhzC +https://imgur.com/a/hYeOL +https://www.google.com/search?q=ctline+%22paragraph+spacing%22&ie=utf-8&oe=utf-8 +http://stackoverflow.com/questions/8010615/how-do-i-determine-the-of-a-ctline-following-a-ctline +https://imgur.com/a/dlpm2 +https://stackoverflow.com/questions/41601845/am-i-missing-something-in-core-text-that-will-let-me-see-which-line-a-certain-po +https://www.google.com/search?q=%22core+text%22+%22combining+character%22&oq=%22core+text%22+%22combining+character%22&gs_l=serp.3...2526898.2528158.0.2528485.21.10.0.0.0.0.216.997.5j3j1.9.0....0...1c.1.64.serp..12.8.904...33i21k1j33i160k1.J69jsB9g0N0 +https://github.com/macvim-dev/macvim/issues/400#issuecomment-274292877 +https://bugs.webkit.org/show_bug.cgi?id=68287 +https://trac.webkit.org/changeset/95391 +https://trac.webkit.org/changeset/95391/trunk/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp +http://stackoverflow.com/questions/41857213/is-there-any-way-i-can-get-precise-metrics-line-ascent-line-descent-etc-of +http://pawlan.com/monica/articles/texttutorial/int.html + +enter in entry fields, possibly other text layout issues, possibly keyboard shortcuts +https://developer.gnome.org/gtk3/3.10/GtkEntry.html "activate" signal +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkentry.c (not sure where) +https://developer.gnome.org/gtk3/3.10/GtkTextView.html signals section of contents +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtktextview.c (not sure where; closed before I could find out again though gitlab might wreck it anyway) +https://developer.gnome.org/gtk3/3.10/gtk3-Bindings.html +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkwidget.c (not sure where) +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkbindings.c (not sure where) +https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/keybindings/ide-keybindings.c 404; TODO find the original cgit link in the chat logs to see what hergertme wanted me to see +https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view.c likewise +https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view-mode.c#n323 likewise + +rgb int->float conversion +https://stackoverflow.com/questions/41348339/how-to-convert-rgb-to-hexadecimal-using-gtk?noredirect=1#comment69903262_41348339 + +windows fonts, hi-dpi +https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size + +windows default fonts +https://stackoverflow.com/questions/41505151/how-to-draw-text-with-the-default-ui-font-in-directwrite#41505750 + +uwp stuff +https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/inking-controls +https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/master-details + +cmake stuff +https://public.kitware.com/pipermail/cmake/2010-January/034669.html +https://cmake.org/cmake/help/v3.0/command/string.html +https://cmake.org/pipermail/cmake/2003-April/003599.html + +opentype features and locales +https://www.microsoft.com/typography/otspec/featurelist.htm +https://en.wikipedia.org/wiki/List_of_typographic_features +https://www.microsoft.com/typography/otspec160/scripttags.htm +https://msdn.microsoft.com/en-us/library/windows/desktop/dd371503(v=vs.85).aspx + +gspell (TODO figure out what I wanted from this; possibly spelling checking) +https://git.gnome.org/browse/gspell/tree/gspell/gspell-inline-checker-text-buffer.c +https://git.gnome.org/browse/gtk+/tree/gtk/gtktextview.c?h=gtk-3-10 diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI index bfec3e7c..14397b67 100644 --- a/_notes/windowsHighDPI +++ b/_notes/windowsHighDPI @@ -7,3 +7,5 @@ https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements- https://channel9.msdn.com/Events/Windows/Windows-Developer-Day-Creators-Update/High-DPI-Improvements-for-Desktop-Developers https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-bbe37ac1b0f0/per-monitor-high-dpi-awareness-wmdpichanged?forum=vcgeneral https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363 +https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size +http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli From e244a0edf4799df3df4b5137fde3821ff3dc0870 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Mar 2018 13:13:31 -0400 Subject: [PATCH 454/487] More notes. --- _notes/misc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/_notes/misc b/_notes/misc index 714a401e..697b2978 100644 --- a/_notes/misc +++ b/_notes/misc @@ -100,3 +100,10 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dd371503(v=vs.85).aspx gspell (TODO figure out what I wanted from this; possibly spelling checking) https://git.gnome.org/browse/gspell/tree/gspell/gspell-inline-checker-text-buffer.c https://git.gnome.org/browse/gtk+/tree/gtk/gtktextview.c?h=gtk-3-10 + +other assorted notes on text +https://mail.gnome.org/archives/gtk-devel-list/2016-March/msg00037.html +https://mail.gnome.org/archives/gtk-devel-list/2016-June/msg00007.html +https://blogs.msdn.microsoft.com/tsfaware/ +https://stackoverflow.com/questions/40453186/what-is-the-correct-modern-way-to-handle-arbitrary-text-input-in-a-custom-contr +https://www.microsoft.com/typography/fonts/family.aspx From 696459be852f08e93e7ee647e5040c36c54da381 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 26 Mar 2018 22:03:17 -0400 Subject: [PATCH 455/487] More notes. --- _notes/misc | 3 +++ _notes/winARM64 | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 _notes/winARM64 diff --git a/_notes/misc b/_notes/misc index 697b2978..0b246a11 100644 --- a/_notes/misc +++ b/_notes/misc @@ -107,3 +107,6 @@ https://mail.gnome.org/archives/gtk-devel-list/2016-June/msg00007.html https://blogs.msdn.microsoft.com/tsfaware/ https://stackoverflow.com/questions/40453186/what-is-the-correct-modern-way-to-handle-arbitrary-text-input-in-a-custom-contr https://www.microsoft.com/typography/fonts/family.aspx + +windows ribbon +https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large) diff --git a/_notes/winARM64 b/_notes/winARM64 new file mode 100644 index 00000000..c22d5593 --- /dev/null +++ b/_notes/winARM64 @@ -0,0 +1,3 @@ +http://www.os2museum.com/wp/windows-10-arm64-on-qemu/ +https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm +http://pete.akeo.ie/2017/05/compiling-desktop-arm-applications-with.html From 177527c959f771a2ceb7d13bc449e8b3fac98b53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 29 Mar 2018 23:36:53 -0400 Subject: [PATCH 456/487] More notes. --- _notes/misc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_notes/misc b/_notes/misc index 0b246a11..3b1f4c00 100644 --- a/_notes/misc +++ b/_notes/misc @@ -110,3 +110,7 @@ https://www.microsoft.com/typography/fonts/family.aspx windows ribbon https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large) + +https://developer.apple.com/library/content/samplecode/CocoaTipsAndTricks/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010039 BlurryView and StaticObjcMethods in particular +printing https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003958 +auto layout https://developer.apple.com/library/content/releasenotes/UserExperience/RNAutomaticLayout/index.html#//apple_ref/doc/uid/TP40010631 From e45c7b4d8750162bbfe23fd27c0fc8cb77171a01 Mon Sep 17 00:00:00 2001 From: Thomas Corwin Date: Mon, 2 Apr 2018 12:47:23 -0400 Subject: [PATCH 457/487] Added the LibUISharp project to the list of bindings. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e94195b4..3de973d8 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ Language | Bindings --- | --- C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike) C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) -C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) +C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [LibUISharp](https://github.com/tom-corwin/LibUISharp) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) From d53ab65589e126bb9bf40c99d67ca1d94bbff738 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 4 Apr 2018 11:27:59 -0400 Subject: [PATCH 458/487] More notes. --- _notes/tableNotes | 4 ++++ _notes/windowsHighDPI | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 _notes/tableNotes diff --git a/_notes/tableNotes b/_notes/tableNotes new file mode 100644 index 00000000..b894d5a3 --- /dev/null +++ b/_notes/tableNotes @@ -0,0 +1,4 @@ +https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Protocols/NSOutlineViewDataSource_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDataSource/outlineView:child:ofItem: +https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOutlineViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDelegate/outlineView:didAddRowView:forRow: + https://developer.apple.com/documentation/appkit/nsoutlineviewdelegate/1528320-outlineview?language=objc +https://github.com/mity/mctrl/blob/master/mctrl/treelist.c around treelist_set_subitem() diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI index 14397b67..0f69a507 100644 --- a/_notes/windowsHighDPI +++ b/_notes/windowsHighDPI @@ -9,3 +9,10 @@ https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-b https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363 https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli + +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx + https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot) +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#appendix_c_common_high_dpi_issues + https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#appendix_c_common_high_dpi_issues +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#addressing_high_dpi_issues + https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#addressing_high_dpi_issues From 1266a77e61d973cc0693054af88fcabd9339ba4a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 7 Apr 2018 23:39:41 -0400 Subject: [PATCH 459/487] More notes. --- _notes/darwinAutoLayout | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 _notes/darwinAutoLayout diff --git a/_notes/darwinAutoLayout b/_notes/darwinAutoLayout new file mode 100644 index 00000000..5a521fc0 --- /dev/null +++ b/_notes/darwinAutoLayout @@ -0,0 +1,6 @@ +https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSLayoutConstraint_Class/ + https://developer.apple.com/documentation/uikit/nslayoutconstraint?language=objc +https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1 + https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1 (Listing 13-3) +https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1 + https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1 ("IMPORTANT Your layout must fully define..." large box) From ceec25f0613422dd29ef6be6bd136496ab61e899 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Apr 2018 21:26:16 -0400 Subject: [PATCH 460/487] More notes. --- _notes/misc | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/_notes/misc b/_notes/misc index 3b1f4c00..2d13d6b8 100644 --- a/_notes/misc +++ b/_notes/misc @@ -114,3 +114,35 @@ https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large) https://developer.apple.com/library/content/samplecode/CocoaTipsAndTricks/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010039 BlurryView and StaticObjcMethods in particular printing https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003958 auto layout https://developer.apple.com/library/content/releasenotes/UserExperience/RNAutomaticLayout/index.html#//apple_ref/doc/uid/TP40010631 + +other stuff, mostly custom draw on windows +https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/multimedia/DirectWrite/ChooseFont/ChooseFont.cpp at ChooseFontDialog::OnFontSizeNameEdit() definition +https://developer.gnome.org/gtk3/3.10/GtkColorButton.html +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx between BS_LEFTTEXT and BS_RADIOBUTTON +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx description + parameters +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx dwItemSpec parameter +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY BS_NOTIFY to BS_RIGHT +http://stackoverflow.com/questions/17678261/how-to-change-color-of-a-button-while-keeping-buttons-functions-in-win32 +https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx switch (pnm->hdr.code){ +http://stackoverflow.com/questions/36756094/will-the-device-context-of-a-child-window-have-the-same-text-metrics-and-text-ex +actually I think most if not all of these were from when I was trying to implement uiColorButton on Windows, so I'm not sure if I still need these, but meh... + +more miscellaneous +https://blogs.msdn.microsoft.com/oldnewthing/20160328-00/?p=93204 from "One is virtual memory" to "occured to them that their" +https://blogs.msdn.microsoft.com/oldnewthing/20160331-00/?p=93231 + https://msdn.microsoft.com/en-us/library/windows/desktop/aa373631(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx From 34fd48ae0d251e38198b83670f1c1b6b73636b94 Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Mon, 9 Apr 2018 18:29:46 +1000 Subject: [PATCH 461/487] Added link to Hedron --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e94195b4..8132289a 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](h C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) -Crystal | [libui.cr](https://github.com/Fusion/libui.cr) +Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Harbour | [HBUI](https://github.com/RJopek/HBUI) From a098f7f78bdfc4262d66b3404636fd06e9a70da3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 9 Apr 2018 21:32:50 -0400 Subject: [PATCH 462/487] More notes. --- _notes/misc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/_notes/misc b/_notes/misc index 2d13d6b8..4f8c1b08 100644 --- a/_notes/misc +++ b/_notes/misc @@ -146,3 +146,8 @@ https://blogs.msdn.microsoft.com/oldnewthing/20160331-00/?p=93231 https://msdn.microsoft.com/en-us/library/windows/desktop/aa373631(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx +https://blogs.msdn.microsoft.com/oldnewthing/20101118-00/?p=12253#comment-875273 "MTA implies poor service and fare hikes." + +from #gtk+ +[18:06:48] if i am doing an animation that needs to be updated at every duration(say every 50ms), should i use get_frame_time() from the frameclock to do that? (from add_tick_callback()) +[18:12:47] <~Company> zorcon: yes From 8e7bd22d6c93ad74644cc4b6aafb3585e8394a40 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 12 Apr 2018 21:55:57 -0400 Subject: [PATCH 463/487] More notes. --- _notes/OS2 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 _notes/OS2 diff --git a/_notes/OS2 b/_notes/OS2 new file mode 100644 index 00000000..e5aa04e4 --- /dev/null +++ b/_notes/OS2 @@ -0,0 +1,25 @@ +https://twitter.com/OS2World/status/983822011389620224 + Hi. I recommend you today to start with OS/2 Warp 4.52 or ArcaOS ( The tools are almost the same of Warp 3). EDM/2 is a good place to start: http://www.edm2.com +https://twitter.com/OS2World/status/983822594465034240 + There is also an RPM with OS/2 Software (https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/ …), and you can get from it some parts of the OS/2 Toolkit. If you want to develop drivers you require the OS/2 Device Driver Kit. (that is more complex). You can also develop in Qt4 and compile things with gcc. +http://www.edm2.com/index.php/Main_Page +https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/ +http://www.edm2.com/index.php/IBM_OS/2_Toolkit_Documentation +https://www.os2world.com/forum/index.php?topic=953.0 +https://www.google.com/search?client=firefox-b-1&ei=QIPPWurPLs7OwAK65K24BA&q=%22OS%2F2+Toolkit%22+site%3Aamazon.com&oq=%22OS%2F2+Toolkit%22+site%3AAmazon.com&gs_l=psy-ab.3...160814.161293.0.161540.2.2.0.0.0.0.128.252.0j2.2.0....0...1c.1.64.psy-ab..0.0.0....0.itT9Og6hC5c +http://www.edm2.com/index.php/List_of_Presentation_Manager_Articles +https://www.ecsoft2.org/ +http://www.edm2.com/index.php/Cairo + http://www.edm2.com/index.php/Doodle +http://www.edm2.com/index.php/Workplace_Shell_Toolkit +http://wpstk.netlabs.org/en/site/index.xml +https://en.wikipedia.org/wiki/Workplace_Shell +https://www.google.com/search?q=OS2+alphablending&ie=utf-8&oe=utf-8&client=firefox-b-1 +http://www.edm2.com/index.php/List_of_Multimedia_Articles +alphablending: + http://www.osnews.com/story/369/Review-eComStation-OS2-1.0/page3/ + http://halfos.ru/documentation/33-os2-api-documentation/67-opengl-os2-developer-reference-guide.html + http://www.altools.com/ALTools/ALSee/ALSee-Image-Viewer.aspx + http://www.os2voice.org/vnewsarc/bn2007122.html + http://www.mozillazine.org/talkback.html?article=194 + https://books.google.com/books?id=9cpU5uYCzq4C&pg=PA202&lpg=PA202&dq=%22OS/2%22+alphablending&source=bl&ots=uatEop2jAL&sig=HAa_ofQSKsk6-8tBR6YZ6MRJG_0&hl=en&sa=X&ved=0ahUKEwiDq5HukLbaAhUk8IMKHR7aCw4Q6AEIWTAI#v=onepage&q=%22OS%2F2%22%20alphablending&f=false From a24085bd5efb2379cb70581be03d2de3b91f1f35 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 Apr 2018 22:30:21 -0400 Subject: [PATCH 464/487] More notes. --- _notes/misc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/_notes/misc b/_notes/misc index 4f8c1b08..413fcdc3 100644 --- a/_notes/misc +++ b/_notes/misc @@ -151,3 +151,19 @@ https://blogs.msdn.microsoft.com/oldnewthing/20101118-00/?p=12253#comment-875273 from #gtk+ [18:06:48] if i am doing an animation that needs to be updated at every duration(say every 50ms), should i use get_frame_time() from the frameclock to do that? (from add_tick_callback()) [18:12:47] <~Company> zorcon: yes + +windows stuff: mostly aero tricks, sdk version macro notes, ribbon stuff, scrollbar stuff too +https://tools.stefankueng.com/SendMessage.html +https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/SendMessage.vcxproj +https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/stdafx.h +https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/MainDlg.h +https://sourceforge.net/u/steveking/profile/ +https://github.com/stefankueng/BowPad/tree/master/src + sourceforge original +https://github.com/stefankueng/sktoolslib/blob/master/AeroControls.cpp + sourceforge original +https://tools.stefankueng.com/BowPad.html +https://www.codeproject.com/Articles/1084/Custom-Scrollbar-Library-version-1-1#_articleTop +https://www.google.com/search?client=firefox-b-1&biw=1080&bih=600&ei=gUHRWsrdMYayggf30bfoAw&q=%22aerocontrols.h%22&oq=%22aerocontrols.h%22&gs_l=psy-ab.3..35i39k1l6.1816.3367.0.3413.4.2.0.0.0.0.0.0..1.0....0...1c.1.64.psy-ab..3.1.211.6...211.0Ppw_OREAk0 + https://searchcode.com/codesearch/view/7295148/ + https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroBaseDlg.h + https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroControls.cpp + https://github.com/gavioto/stexbar/blob/master/Misc/FileTool/src/CleanVerifyDlg.h From 03d0d4074a0d43d987b915f0f93173986b4ab02c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 Apr 2018 12:36:52 -0400 Subject: [PATCH 465/487] More notes. --- _notes/misc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/_notes/misc b/_notes/misc index 413fcdc3..5d40d908 100644 --- a/_notes/misc +++ b/_notes/misc @@ -167,3 +167,20 @@ https://www.google.com/search?client=firefox-b-1&biw=1080&bih=600&ei=gUHRWsrdMYa https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroBaseDlg.h https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroControls.cpp https://github.com/gavioto/stexbar/blob/master/Misc/FileTool/src/CleanVerifyDlg.h + +windows ribbon colors and dwm customization +https://github.com/stefankueng/BowPad/blob/master/src/MainWindow.cpp +https://msdn.microsoft.com/en-us/library/windows/desktop/dd940487(v=vs.85).aspx +https://github.com/yohei-yoshihara/GameOfLife3D/blob/master/GameOfLife3DLib/gameoflife3d/Ribbon.cpp +https://msdn.microsoft.com/en-us/library/windows/desktop/dd940404(v=vs.85).aspx +https://github.com/stefankueng/sktoolslib/blob/master/AeroColors.cpp +https://gist.github.com/emoacht/bfa852ccc16bdb5465bd +https://stackoverflow.com/questions/4258295/aero-how-to-draw-solid-opaque-colors-on-glass +https://github.com/ComponentFactory/Krypton +https://www.codeproject.com/Articles/620045/Custom-Controls-in-Win-API-Visual-Styles +https://blogs.msdn.microsoft.com/wpf/2010/11/30/systemcolors-reference/ +https://stackoverflow.com/questions/25639621/check-when-a-user-changes-windows-glass-brush-theme-color +https://msdn.microsoft.com/en-us/library/windows/desktop/aa969513(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/aa969527(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd388198(v=vs.85).aspx +I hope the MFC ribbon can have customizable colors too, otherwise I'll have to do some serious image processing... From e0f800d5eb016c4c1a128b081a2eef092ce50a07 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 15:39:28 -0400 Subject: [PATCH 466/487] Started cleaning up the common/ folder. Backed up the current uipriv.h. --- common/OLD_uipriv.h | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 common/OLD_uipriv.h diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h new file mode 100644 index 00000000..02676a16 --- /dev/null +++ b/common/OLD_uipriv.h @@ -0,0 +1,66 @@ +// 6 april 2015 + +// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors +#include "utf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "controlsigs.h" + +extern uiInitOptions options; + +extern void *uiAlloc(size_t, const char *); +#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) +extern void *uiRealloc(void *, size_t, const char *); +extern void uiFree(void *); + +// ugh, this was only introduced in MSVC 2015... +#ifdef _MSC_VER +#define __func__ __FUNCTION__ +#endif +extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); +#define _ns2(s) #s +#define _ns(s) _ns2(s) +extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); +#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) +extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); +#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) + +// control.c +extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); + +// shouldquit.c +extern int shouldQuit(void); + +// areaevents.c +typedef struct clickCounter clickCounter; +// you should call Reset() to zero-initialize a new instance +// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly +struct clickCounter { + int curButton; + int rectX0; + int rectY0; + int rectX1; + int rectY1; + uintptr_t prevTime; + int count; +}; +int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); +extern void clickCounterReset(clickCounter *); +extern int fromScancode(uintptr_t, uiAreaKeyEvent *); + +// matrix.c +extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); +extern void scaleCenter(double, double, double *, double *); +extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); + +// OS-specific text.* files +extern int uiprivStricmp(const char *a, const char *b); + +#ifdef __cplusplus +} +#endif From 4a57b15d09179baf3192467c1b50bdf42f68b3ed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 15:49:45 -0400 Subject: [PATCH 467/487] Renamed the common options variable to uiprivOptions. --- common/OLD_uipriv.h | 18 ------------------ common/uipriv.h | 11 ++++------- darwin/main.m | 4 ++-- unix/main.c | 4 ++-- windows/init.cpp | 6 +++--- 5 files changed, 11 insertions(+), 32 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 02676a16..453871d7 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,17 +1,3 @@ -// 6 april 2015 - -// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors -#include "utf.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "controlsigs.h" - -extern uiInitOptions options; extern void *uiAlloc(size_t, const char *); #define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) @@ -60,7 +46,3 @@ extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); // OS-specific text.* files extern int uiprivStricmp(const char *a, const char *b); - -#ifdef __cplusplus -} -#endif diff --git a/common/uipriv.h b/common/uipriv.h index 02676a16..5f0f287d 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,17 +1,14 @@ // 6 april 2015 - -// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors +#include +#include +#include "controlsigs.h" #include "utf.h" #ifdef __cplusplus extern "C" { #endif -#include -#include -#include "controlsigs.h" - -extern uiInitOptions options; +extern uiInitOptions uiprivOptions; extern void *uiAlloc(size_t, const char *); #define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) diff --git a/darwin/main.m b/darwin/main.m index 865fb942..1bc6bfc3 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -106,12 +106,12 @@ static BOOL stepsIsRunning; @end -uiInitOptions options; +uiInitOptions uiprivOptions; const char *uiInit(uiInitOptions *o) { @autoreleasepool { - options = *o; + uiprivOptions = *o; app = [[applicationClass sharedApplication] retain]; // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! // see https://github.com/andlabs/ui/issues/6 diff --git a/unix/main.c b/unix/main.c index 2998bf31..1d7e7a76 100644 --- a/unix/main.c +++ b/unix/main.c @@ -1,14 +1,14 @@ // 6 april 2015 #include "uipriv_unix.h" -uiInitOptions options; +uiInitOptions uiprivOptions; const char *uiInit(uiInitOptions *o) { GError *err = NULL; const char *msg; - options = *o; + uiprivOptions = *o; if (gtk_init_with_args(NULL, NULL, NULL, NULL, NULL, &err) == FALSE) { msg = g_strdup(err->message); g_error_free(err); diff --git a/windows/init.cpp b/windows/init.cpp index cfe63b9a..2c25d3d4 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -39,8 +39,8 @@ static const char *initerr(const char *message, const WCHAR *label, DWORD value) #define ieLastErr(msg) initerr("=" msg, L"GetLastError() ==", GetLastError()) #define ieHRESULT(msg, hr) initerr("=" msg, L"HRESULT", (DWORD) hr) -// LONGTERM make common -uiInitOptions options; +// LONGTERM put this declaration in a common file +uiInitOptions uiprivOptions; #define wantedICCClasses ( \ ICC_STANDARD_CLASSES | /* user32.dll controls */ \ @@ -62,7 +62,7 @@ const char *uiInit(uiInitOptions *o) INITCOMMONCONTROLSEX icc; HRESULT hr; - options = *o; + uiprivOptions = *o; initAlloc(); From 72e8b9a1983801fb7ab65de35011de85ecd734ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 15:53:27 -0400 Subject: [PATCH 468/487] Started FINALLY renaming uiAlloc(), uiNew(), uiRealloc(), and uiFree() into uipriv* forms. This handles the common folder. --- common/OLD_uipriv.h | 5 ----- common/attribute.c | 2 +- common/attrstr.h | 6 ------ common/control.c | 4 ++-- common/uipriv.h | 8 ++++---- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 453871d7..64d82ad1 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,9 +1,4 @@ -extern void *uiAlloc(size_t, const char *); -#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) -extern void *uiRealloc(void *, size_t, const char *); -extern void uiFree(void *); - // ugh, this was only introduced in MSVC 2015... #ifdef _MSC_VER #define __func__ __FUNCTION__ diff --git a/common/attribute.c b/common/attribute.c index 5054d1c1..4a8f0160 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -85,7 +85,7 @@ uiAttribute *uiNewFamilyAttribute(const char *family) uiAttribute *a; a = newAttribute(uiAttributeTypeFamily); - a->u.family = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)"); + a->u.family = (char *) uiprivAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)"); strcpy(a->u.family, family); return a; } diff --git a/common/attrstr.h b/common/attrstr.h index 2f4e42b4..54e43feb 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,11 +1,5 @@ // 19 february 2018 -// TODO remove when done migrating these functions -#define uiprivNew(x) uiNew(x) -#define uiprivAlloc(x, y) uiAlloc(x, y) -#define uiprivRealloc(x, y, z) uiRealloc(x, y, z) -#define uiprivFree(x) uiFree(x) - // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); extern void uiprivAttributeRelease(uiAttribute *a); diff --git a/common/control.c b/common/control.c index 28066461..e46e322c 100644 --- a/common/control.c +++ b/common/control.c @@ -63,7 +63,7 @@ uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const c { uiControl *c; - c = (uiControl *) uiAlloc(size, typenamestr); + c = (uiControl *) uiprivAlloc(size, typenamestr); c->Signature = uiControlSignature; c->OSSignature = OSsig; c->TypeSignature = typesig; @@ -74,7 +74,7 @@ void uiFreeControl(uiControl *c) { if (uiControlParent(c) != NULL) userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); - uiFree(c); + uiprivFree(c); } void uiControlVerifySetParent(uiControl *c, uiControl *parent) diff --git a/common/uipriv.h b/common/uipriv.h index 5f0f287d..69062c87 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -10,10 +10,10 @@ extern "C" { extern uiInitOptions uiprivOptions; -extern void *uiAlloc(size_t, const char *); -#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) -extern void *uiRealloc(void *, size_t, const char *); -extern void uiFree(void *); +extern void *uiprivAlloc(size_t, const char *); +#define uiprivNew(T) ((T *) uiprivAlloc(sizeof (T), #T)) +extern void *uiprivRealloc(void *, size_t, const char *); +extern void uiprivFree(void *); // ugh, this was only introduced in MSVC 2015... #ifdef _MSC_VER From 8ca32f098fffd17e3209bb6cfb239432fcd6238e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 16:05:24 -0400 Subject: [PATCH 469/487] uiAlloc() et al -> uiprivAlloc() et al, OS X code. --- darwin/alloc.m | 12 ++++++------ darwin/draw.m | 20 ++++++++++---------- darwin/fontmatch.m | 4 ++-- darwin/fontvariation.m | 4 ++-- darwin/grid.m | 32 ++++++++++++++++---------------- darwin/image.m | 10 +++++----- darwin/map.m | 4 ++-- darwin/menu.m | 8 ++++---- darwin/scrollview.m | 4 ++-- 9 files changed, 49 insertions(+), 49 deletions(-) diff --git a/darwin/alloc.m b/darwin/alloc.m index 0bbb8996..0012c853 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -41,7 +41,7 @@ void uninitAlloc(void) [str release]; } -void *uiAlloc(size_t size, const char *type) +void *uiprivAlloc(size_t size, const char *type) { void *out; @@ -57,17 +57,17 @@ void *uiAlloc(size_t size, const char *type) return DATA(out); } -void *uiRealloc(void *p, size_t new, const char *type) +void *uiprivRealloc(void *p, size_t new, const char *type) { void *out; size_t *s; if (p == NULL) - return uiAlloc(new, type); + return uiprivAlloc(new, type); p = BASE(p); out = realloc(p, EXTRA + new); if (out == NULL) { - fprintf(stderr, "memory exhausted in uiRealloc()\n"); + fprintf(stderr, "memory exhausted in uiprivRealloc()\n"); abort(); } s = SIZE(out); @@ -79,10 +79,10 @@ void *uiRealloc(void *p, size_t new, const char *type) return DATA(out); } -void uiFree(void *p) +void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiFree(NULL)"); + implbug("attempt to uiprivFree(NULL)"); p = BASE(p); free(p); [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/darwin/draw.m b/darwin/draw.m index b4be6e7f..b52b5a57 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -12,7 +12,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) { uiDrawPath *p; - p = uiNew(uiDrawPath); + p = uiprivNew(uiDrawPath); p->path = CGPathCreateMutable(); p->fillMode = mode; return p; @@ -21,7 +21,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) void uiDrawFreePath(uiDrawPath *p) { CGPathRelease((CGPathRef) (p->path)); - uiFree(p); + uiprivFree(p); } void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) @@ -108,7 +108,7 @@ uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) { uiDrawContext *c; - c = uiNew(uiDrawContext); + c = uiprivNew(uiDrawContext); c->c = ctxt; c->height = height; return c; @@ -116,7 +116,7 @@ uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) void freeContext(uiDrawContext *c) { - uiFree(c); + uiprivFree(c); } // a stroke is identical to a fill of a stroked path @@ -160,7 +160,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro // create a temporary path identical to the previous one dashPath = (CGPathRef) path->path; if (p->NumDashes != 0) { - dashes = (CGFloat *) uiAlloc(p->NumDashes * sizeof (CGFloat), "CGFloat[]"); + dashes = (CGFloat *) uiprivAlloc(p->NumDashes * sizeof (CGFloat), "CGFloat[]"); for (i = 0; i < p->NumDashes; i++) dashes[i] = p->Dashes[i]; dashPath = CGPathCreateCopyByDashingPath(path->path, @@ -168,7 +168,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro p->DashPhase, dashes, p->NumDashes); - uiFree(dashes); + uiprivFree(dashes); } // the documentation is wrong: this produces a path suitable for calling CGPathCreateCopyByStrokingPath(), not for filling directly // the cast is safe; we never modify the CGPathRef and always cast it back to a CGPathRef anyway @@ -224,8 +224,8 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) // TODO add NULL check to other uses of CGColorSpace // make the gradient - colors = uiAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]"); - locations = uiAlloc(b->NumStops * sizeof (CGFloat), "CGFloat[]"); + colors = uiprivAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]"); + locations = uiprivAlloc(b->NumStops * sizeof (CGFloat), "CGFloat[]"); for (i = 0; i < b->NumStops; i++) { colors[i * 4 + 0] = b->Stops[i].R; colors[i * 4 + 1] = b->Stops[i].G; @@ -234,8 +234,8 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) locations[i] = b->Stops[i].Pos; } gradient = CGGradientCreateWithColorComponents(colorspace, colors, locations, b->NumStops); - uiFree(locations); - uiFree(colors); + uiprivFree(locations); + uiprivFree(colors); // because we're mucking with clipping, we need to save the graphics state and restore it later CGContextSaveGState(ctxt); diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index ea075e53..2c240b95 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -365,7 +365,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiFontDescrip if ([d variation] != NULL) axisDict = uiprivMakeVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); - closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + closeness = (struct closeness *) uiprivAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { uiFontDescriptor fields; @@ -409,7 +409,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiFontDescrip // release everything if (axisDict != nil) [axisDict release]; - uiFree(closeness); + uiprivFree(closeness); CFRelease(matching); // and release the original descriptor since we no longer need it CFRelease(against); diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index b5a3591a..6dfb3ab1 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -187,7 +187,7 @@ static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) } nEntries = nextuint16be(); *n = nEntries * 2; - entries = (fixed1616 *) uiAlloc(*n * sizeof (fixed1616), "fixed1616[]"); + entries = (fixed1616 *) uiprivAlloc(*n * sizeof (fixed1616), "fixed1616[]"); p = entries; for (i = 0; i < *n; i++) { *p++ = fixed214ToFixed1616((fixed214) nextuint16be()); @@ -247,7 +247,7 @@ fail: - (void)dealloc { if (self->avarMappings != NULL) { - uiFree(self->avarMappings); + uiprivFree(self->avarMappings); self->avarMappings = NULL; } [super dealloc]; diff --git a/darwin/grid.m b/darwin/grid.m index d5c5fb1e..fc98cc9f 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -288,11 +288,11 @@ struct uiGrid { // now build a topological map of the grid gg[y][x] // also figure out which cells contain spanned views so they can be ignored later // treat hidden controls by keeping the indices -1 - gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]"); - gspan = (BOOL **) uiAlloc(ycount * sizeof (BOOL *), "BOOL[][]"); + gg = (int **) uiprivAlloc(ycount * sizeof (int *), "int[][]"); + gspan = (BOOL **) uiprivAlloc(ycount * sizeof (BOOL *), "BOOL[][]"); for (y = 0; y < ycount; y++) { - gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]"); - gspan[y] = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); + gg[y] = (int *) uiprivAlloc(xcount * sizeof (int), "int[]"); + gspan[y] = (BOOL *) uiprivAlloc(xcount * sizeof (BOOL), "BOOL[]"); for (x = 0; x < xcount; x++) gg[y][x] = -1; // empty } @@ -344,9 +344,9 @@ struct uiGrid { // now build a topological map of the grid's views gv[y][x] // for any empty cell, create a dummy view - gv = (NSView ***) uiAlloc(ycount * sizeof (NSView **), "NSView *[][]"); + gv = (NSView ***) uiprivAlloc(ycount * sizeof (NSView **), "NSView *[][]"); for (y = 0; y < ycount; y++) { - gv[y] = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]"); + gv[y] = (NSView **) uiprivAlloc(xcount * sizeof (NSView *), "NSView *[]"); for (x = 0; x < xcount; x++) if (gg[y][x] == -1) { gv[y][x] = [[NSView alloc] initWithFrame:NSZeroRect]; @@ -360,8 +360,8 @@ struct uiGrid { } // now figure out which rows and columns really expand - hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); - vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]"); + hexpand = (BOOL *) uiprivAlloc(xcount * sizeof (BOOL), "BOOL[]"); + vexpand = (BOOL *) uiprivAlloc(ycount * sizeof (BOOL), "BOOL[]"); // first, which don't span for (gc in self->children) { if (!uiControlVisible(gc.c)) @@ -522,16 +522,16 @@ struct uiGrid { // TODO make all expanding rows/columns the same height/width // and finally clean up - uiFree(hexpand); - uiFree(vexpand); + uiprivFree(hexpand); + uiprivFree(vexpand); for (y = 0; y < ycount; y++) { - uiFree(gg[y]); - uiFree(gv[y]); - uiFree(gspan[y]); + uiprivFree(gg[y]); + uiprivFree(gv[y]); + uiprivFree(gspan[y]); } - uiFree(gg); - uiFree(gv); - uiFree(gspan); + uiprivFree(gg); + uiprivFree(gv); + uiprivFree(gspan); } - (void)append:(gridChild *)gc diff --git a/darwin/image.m b/darwin/image.m index b62de31d..aa1b945b 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -11,23 +11,23 @@ uiImage *uiNewImage(double width, double height) { uiImage *i; - i = uiNew(uiImage); + i = uiprivNew(uiImage); i->size = NSMakeSize(width, height); i->i = [[NSImage alloc] initWithSize:i->size]; i->swizzled = [NSMutableArray new]; return i; } -void uiFreeImage(uiImage *i) +void Image(uiImage *i) { NSValue *v; [i->i release]; // to be safe, do this after releasing the image for (v in i->swizzled) - uiFree([v pointerValue]); + uiprivFree([v pointerValue]); [i->swizzled release]; - uiFree(i); + uiprivFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) @@ -40,7 +40,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in // OS X demands that R and B are in the opposite order from what we expect // we must swizzle :( // LONGTERM test on a big-endian system - swizzled = (uint8_t *) uiAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]"); + swizzled = (uint8_t *) uiprivAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]"); bp = (uint8_t *) pixels; sp = swizzled; for (y = 0; y < pixelHeight * pixelStride; y += pixelStride) diff --git a/darwin/map.m b/darwin/map.m index 46a7b8d2..4eaa057d 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -12,7 +12,7 @@ struct mapTable *newMap(void) { struct mapTable *m; - m = uiNew(struct mapTable); + m = uiprivNew(struct mapTable); m->m = [[NSMapTable alloc] initWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) valueOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) capacity:0]; @@ -24,7 +24,7 @@ void mapDestroy(struct mapTable *m) if ([m->m count] != 0) implbug("attempt to destroy map with items inside"); [m->m release]; - uiFree(m); + uiprivFree(m); } void *mapGet(struct mapTable *m, void *key) diff --git a/darwin/menu.m b/darwin/menu.m index 735cac50..11a98e63 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -241,7 +241,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) if (menusFinalized) userbug("You can't create a new menu item after menus have been finalized."); - item = uiNew(uiMenuItem); + item = uiprivNew(uiMenuItem); item->type = type; switch (item->type) { @@ -319,7 +319,7 @@ uiMenu *uiNewMenu(const char *name) if (menus == nil) menus = [NSMutableArray new]; - m = uiNew(uiMenu); + m = uiprivNew(uiMenu); m->menu = [[NSMenu alloc] initWithTitle:toNSString(name)]; // use automatic menu item enabling for all menus for consistency's sake @@ -359,10 +359,10 @@ void uninitMenus(void) v = (NSValue *) obj; mi = (uiMenuItem *) [v pointerValue]; - uiFree(mi); + uiprivFree(mi); }]; [m->items release]; - uiFree(m); + uiprivFree(m); }]; [menus release]; } diff --git a/darwin/scrollview.m b/darwin/scrollview.m index b0b4040c..b583a00f 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -39,7 +39,7 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa [sv setAllowsMagnification:NO]; [sv setDocumentView:p->DocumentView]; - d = uiNew(struct scrollViewData); + d = uiprivNew(struct scrollViewData); scrollViewSetScrolling(sv, d, p->HScroll, p->VScroll); *dout = d; @@ -57,5 +57,5 @@ void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hsc void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d) { - uiFree(d); + uiprivFree(d); } From 099c4ff631911cacc435dd1972561ef847562f3e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 16:36:03 -0400 Subject: [PATCH 470/487] uiAlloc() et al -> uiprivAlloc() et al, GTK+ code. --- unix/alloc.c | 14 +++++++------- unix/child.c | 4 ++-- unix/draw.c | 4 ++-- unix/drawpath.c | 4 ++-- unix/graphemes.c | 10 +++++----- unix/image.c | 8 ++++---- unix/main.c | 2 +- unix/menu.c | 12 ++++++------ 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/unix/alloc.c b/unix/alloc.c index 0291bd49..98eb0e32 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -43,7 +43,7 @@ void uninitAlloc(void) g_free(str); } -void *uiAlloc(size_t size, const char *type) +void *uiprivAlloc(size_t size, const char *type) { void *out; @@ -54,13 +54,13 @@ void *uiAlloc(size_t size, const char *type) return DATA(out); } -void *uiRealloc(void *p, size_t new, const char *type) +void *uiprivRealloc(void *p, size_t new, const char *type) { void *out; size_t *s; if (p == NULL) - return uiAlloc(new, type); + return uiprivAlloc(new, type); p = BASE(p); out = g_realloc(p, EXTRA + new); s = SIZE(out); @@ -68,17 +68,17 @@ void *uiRealloc(void *p, size_t new, const char *type) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiRealloc()", p); + implbug("%p not found in allocations array in uiprivRealloc()", p); g_ptr_array_add(allocations, out); return DATA(out); } -void uiFree(void *p) +void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiFree(NULL)"); + implbug("attempt to uiprivFree(NULL)"); p = BASE(p); g_free(p); if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiFree()", p); + implbug("%p not found in allocations array in uiprivFree()", p); } diff --git a/unix/child.c b/unix/child.c index b4a09677..b6e48807 100644 --- a/unix/child.c +++ b/unix/child.c @@ -33,7 +33,7 @@ struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parent if (child == NULL) return NULL; - c = uiNew(struct child); + c = uiprivNew(struct child); c->c = child; c->widget = GTK_WIDGET(uiControlHandle(c->c)); @@ -82,7 +82,7 @@ void childRemove(struct child *c) if (c->box != NULL) gtk_widget_destroy(c->box); - uiFree(c); + uiprivFree(c); } void childDestroy(struct child *c) diff --git a/unix/draw.c b/unix/draw.c index b4d18049..1eacee33 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -6,7 +6,7 @@ uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) { uiDrawContext *c; - c = uiNew(uiDrawContext); + c = uiprivNew(uiDrawContext); c->cr = cr; c->style = style; return c; @@ -15,7 +15,7 @@ uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) void freeContext(uiDrawContext *c) { // free neither cr nor style; we own neither - uiFree(c); + uiprivFree(c); } static cairo_pattern_t *mkbrush(uiDrawBrush *b) diff --git a/unix/drawpath.c b/unix/drawpath.c index a0165fb8..384743dc 100644 --- a/unix/drawpath.c +++ b/unix/drawpath.c @@ -28,7 +28,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) { uiDrawPath *p; - p = uiNew(uiDrawPath); + p = uiprivNew(uiDrawPath); p->pieces = g_array_new(FALSE, TRUE, sizeof (struct piece)); p->fillMode = mode; return p; @@ -37,7 +37,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) void uiDrawFreePath(uiDrawPath *p) { g_array_free(p->pieces, TRUE); - uiFree(p); + uiprivFree(p); } static void add(uiDrawPath *p, struct piece *piece) diff --git a/unix/graphemes.c b/unix/graphemes.c index 296e655f..b5edb95a 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -16,11 +16,11 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) size_t i; size_t *op; - g = uiNew(struct graphemes); + g = uiprivNew(struct graphemes); // TODO see if we can use the utf routines lenchars = g_utf8_strlen(text, -1); - logattrs = (PangoLogAttr *) uiAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[] (graphemes)"); + logattrs = (PangoLogAttr *) uiprivAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[] (graphemes)"); pango_get_log_attrs(text, len, -1, NULL, logattrs, lenchars + 1); @@ -31,8 +31,8 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) if (logattrs[i].is_cursor_position != 0) g->len++; - g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->pointsToGraphemes = (size_t *) uiprivAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiprivAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); // compute the graphemesToPoints array // TODO merge with the next for loop somehow? @@ -58,6 +58,6 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) // and do the last one *op++ = i; - uiFree(logattrs); + uiprivFree(logattrs); return g; } diff --git a/unix/image.c b/unix/image.c index a79e550f..1bdf0d64 100644 --- a/unix/image.c +++ b/unix/image.c @@ -14,14 +14,14 @@ static void freeImageRep(gpointer item) buf = cairo_image_surface_get_data(cs); cairo_surface_destroy(cs); - uiFree(buf); + uiprivFree(buf); } uiImage *uiNewImage(double width, double height) { uiImage *i; - i = uiNew(uiImage); + i = uiprivNew(uiImage); i->width = width; i->height = height; i->images = g_ptr_array_new_with_free_func(freeImageRep); @@ -31,7 +31,7 @@ uiImage *uiNewImage(double width, double height) void uiFreeImage(uiImage *i) { g_ptr_array_free(i->images, TRUE); - uiFree(i); + uiprivFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) @@ -43,7 +43,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in int y; cstride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth); - buf = (unsigned char *) uiAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]"); + buf = (unsigned char *) uiprivAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]"); p = buf; for (y = 0; y < pixelStride * pixelHeight; y += pixelStride) { memmove(p, src + y, cstride); diff --git a/unix/main.c b/unix/main.c index 1d7e7a76..a0cd7835 100644 --- a/unix/main.c +++ b/unix/main.c @@ -99,7 +99,7 @@ void uiQueueMain(void (*f)(void *data), void *data) { struct queued *q; - // we have to use g_new0()/g_free() because uiAlloc() is only safe to call on the main thread + // we have to use g_new0()/g_free() because uiprivAlloc() is only safe to call on the main thread // for some reason it didn't affect me, but it did affect krakjoe q = g_new0(struct queued, 1); q->f = f; diff --git a/unix/menu.c b/unix/menu.c index 5ccb4a51..1d950c5f 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -137,7 +137,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) if (menusFinalized) userbug("You cannot create a new menu item after menus have been finalized."); - item = uiNew(uiMenuItem); + item = uiprivNew(uiMenuItem); g_array_append_val(m->items, item); @@ -234,7 +234,7 @@ uiMenu *uiNewMenu(const char *name) if (menus == NULL) menus = g_array_new(FALSE, TRUE, sizeof (uiMenu *)); - m = uiNew(uiMenu); + m = uiprivNew(uiMenu); g_array_append_val(menus, m); @@ -260,7 +260,7 @@ static void appendMenuItem(GtkMenuShell *submenu, uiMenuItem *item, uiWindow *w) singleSetChecked(GTK_CHECK_MENU_ITEM(menuitem), item->checked, signal); } gtk_menu_shell_append(submenu, menuitem); - ww = uiNew(struct menuItemWindow); + ww = uiprivNew(struct menuItemWindow); ww->w = w; ww->signal = signal; g_hash_table_insert(item->windows, menuitem, ww); @@ -309,7 +309,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data) w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget); if (g_hash_table_remove(item->windows, widget) == FALSE) implbug("GtkMenuItem %p not in menu item's item/window map", widget); - uiFree(w); + uiprivFree(w); fmi->i++; } @@ -357,10 +357,10 @@ void uninitMenus(void) implbug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); g_free(item->name); g_hash_table_destroy(item->windows); - uiFree(item); + uiprivFree(item); } g_array_free(m->items, TRUE); - uiFree(m); + uiprivFree(m); } g_array_free(menus, TRUE); } From c6bb4636929f146b557521e22854c3f86e683f88 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 18:12:58 -0400 Subject: [PATCH 471/487] uiAlloc() et al -> uiprivAlloc() et al, Windows code. --- windows/alloc.cpp | 10 +++++----- windows/attrstr.cpp | 2 +- windows/button.cpp | 2 +- windows/checkbox.cpp | 2 +- windows/colordialog.cpp | 20 ++++++++++---------- windows/combobox.cpp | 2 +- windows/datetimepicker.cpp | 14 +++++++------- windows/debug.cpp | 10 +++++----- windows/draw.cpp | 12 ++++++------ windows/drawpath.cpp | 4 ++-- windows/drawtext.cpp | 10 +++++----- windows/dwrite.cpp | 2 +- windows/editablecombo.cpp | 2 +- windows/fontdialog.cpp | 20 ++++++++++---------- windows/form.cpp | 2 +- windows/graphemes.cpp | 6 +++--- windows/grid.cpp | 4 ++-- windows/group.cpp | 2 +- windows/init.cpp | 6 +++--- windows/label.cpp | 2 +- windows/menu.cpp | 24 ++++++++++++------------ windows/multilineentry.cpp | 6 +++--- windows/radiobuttons.cpp | 2 +- windows/spinbox.cpp | 4 ++-- windows/stddialogs.cpp | 4 ++-- windows/tab.cpp | 2 +- windows/tabpage.cpp | 4 ++-- windows/text.cpp | 16 ++++++++-------- windows/uipriv_windows.hpp | 4 ++-- windows/utf16.cpp | 10 +++++----- windows/window.cpp | 2 +- 31 files changed, 106 insertions(+), 106 deletions(-) diff --git a/windows/alloc.cpp b/windows/alloc.cpp index 23201f75..244b2380 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -27,7 +27,7 @@ void uninitAlloc(void) #define rawBytes(pa) (&((*pa)[0])) -void *uiAlloc(size_t size, const char *type) +void *uiprivAlloc(size_t size, const char *type) { byteArray *out; @@ -37,13 +37,13 @@ void *uiAlloc(size_t size, const char *type) return rawBytes(out); } -void *uiRealloc(void *_p, size_t size, const char *type) +void *uiprivRealloc(void *_p, size_t size, const char *type) { uint8_t *p = (uint8_t *) _p; byteArray *arr; if (p == NULL) - return uiAlloc(size, type); + return uiprivAlloc(size, type); arr = heap[p]; // TODO does this fill in? arr->resize(size, 0); @@ -52,12 +52,12 @@ void *uiRealloc(void *_p, size_t size, const char *type) return rawBytes(arr); } -void uiFree(void *_p) +void uiprivFree(void *_p) { uint8_t *p = (uint8_t *) _p; if (p == NULL) - implbug("attempt to uiFree(NULL)"); + implbug("attempt to uiprivFree(NULL)"); types.erase(heap[p]); delete heap[p]; heap.erase(p); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 9ea0f755..740ac43e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -295,7 +295,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute hr = p->layout->SetFontFamilyName(wfamily, range); if (hr != S_OK) logHRESULT(L"error applying family name attribute", hr); - uiFree(wfamily); + uiprivFree(wfamily); break; case uiAttributeTypeSize: hr = p->layout->SetFontSize( diff --git a/windows/button.cpp b/windows/button.cpp index 3b12e726..d8913ec7 100644 --- a/windows/button.cpp +++ b/windows/button.cpp @@ -95,7 +95,7 @@ uiButton *uiNewButton(const char *text) BS_PUSHBUTTON | WS_TABSTOP, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); uiWindowsRegisterWM_COMMANDHandler(b->hwnd, onWM_COMMAND, uiControl(b)); uiButtonOnClicked(b, defaultOnClicked, NULL); diff --git a/windows/checkbox.cpp b/windows/checkbox.cpp index be425c00..3d8c92e3 100644 --- a/windows/checkbox.cpp +++ b/windows/checkbox.cpp @@ -108,7 +108,7 @@ uiCheckbox *uiNewCheckbox(const char *text) BS_CHECKBOX | WS_TABSTOP, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); uiWindowsRegisterWM_COMMANDHandler(c->hwnd, onWM_COMMAND, uiControl(c)); uiCheckboxOnToggled(c, defaultOnToggled, NULL); diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 2efe72c8..d3d9bde9 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -228,7 +228,7 @@ static void updateDouble(HWND hwnd, double d, HWND whichChanged) return; str = ftoutf16(d); setWindowText(hwnd, str); - uiFree(str); + uiprivFree(str); } static void updateDialog(struct colorDialog *c, HWND whichChanged) @@ -259,22 +259,22 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) if (whichChanged != c->editRInt) { str = itoutf16(rb); setWindowText(c->editRInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editGInt) { str = itoutf16(gb); setWindowText(c->editGInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editBInt) { str = itoutf16(bb); setWindowText(c->editBInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editAInt) { str = itoutf16(ab); setWindowText(c->editAInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editHex) { @@ -956,7 +956,7 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) { struct colorDialog *c; - c = uiNew(struct colorDialog); + c = uiprivNew(struct colorDialog); c->hwnd = hwnd; c->out = (struct colorDialogRGBA *) lParam; // load initial values now @@ -995,7 +995,7 @@ static void endColorDialog(struct colorDialog *c, INT_PTR code) { if (EndDialog(c->hwnd, code) == 0) logLastError(L"error ending color dialog"); - uiFree(c); + uiprivFree(c); } // TODO make this void on the font dialog too @@ -1020,7 +1020,7 @@ static double editDouble(HWND hwnd) s = windowText(hwnd); d = _wtof(s); - uiFree(s); + uiprivFree(s); return d; } @@ -1111,7 +1111,7 @@ static int editInt(HWND hwnd) s = windowText(hwnd); i = _wtoi(s); - uiFree(s); + uiprivFree(s); return i; } @@ -1176,7 +1176,7 @@ static void hexChanged(struct colorDialog *c) buf = windowText(c->editHex); is = hex2RGBA(buf, &r, &g, &b, &a); - uiFree(buf); + uiprivFree(buf); if (!is) return; rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); diff --git a/windows/combobox.cpp b/windows/combobox.cpp index 87c999ea..50f49dd7 100644 --- a/windows/combobox.cpp +++ b/windows/combobox.cpp @@ -66,7 +66,7 @@ void uiComboboxAppend(uiCombobox *c, const char *text) logLastError(L"error appending item to uiCombobox"); else if (res == (LRESULT) CB_ERRSPACE) logLastError(L"memory exhausted appending item to uiCombobox"); - uiFree(wtext); + uiprivFree(wtext); } int uiComboboxSelected(uiCombobox *c) diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index e105c2fd..342eb256 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -19,7 +19,7 @@ static WCHAR *expandYear(WCHAR *dts, int n) int ny = 0; // allocate more than we need to be safe - out = (WCHAR *) uiAlloc((n * 3) * sizeof (WCHAR), "WCHAR[]"); + out = (WCHAR *) uiprivAlloc((n * 3) * sizeof (WCHAR), "WCHAR[]"); q = out; for (p = dts; *p != L'\0'; p++) { // first, if the current character is a y, increment the number of consecutive ys @@ -73,17 +73,17 @@ static void setDateTimeFormat(HWND hwnd) ndate = GLI(LOCALE_SSHORTDATE, NULL, 0); if (ndate == 0) logLastError(L"error getting date string length"); - date = (WCHAR *) uiAlloc(ndate * sizeof (WCHAR), "WCHAR[]"); + date = (WCHAR *) uiprivAlloc(ndate * sizeof (WCHAR), "WCHAR[]"); if (GLI(LOCALE_SSHORTDATE, date, ndate) == 0) logLastError(L"error geting date string"); unexpandedDate = date; // so we can free it date = expandYear(unexpandedDate, ndate); - uiFree(unexpandedDate); + uiprivFree(unexpandedDate); ntime = GLI(LOCALE_STIMEFORMAT, NULL, 0); if (ndate == 0) logLastError(L"error getting time string length"); - time = (WCHAR *) uiAlloc(ntime * sizeof (WCHAR), "WCHAR[]"); + time = (WCHAR *) uiprivAlloc(ntime * sizeof (WCHAR), "WCHAR[]"); if (GLI(LOCALE_STIMEFORMAT, time, ntime) == 0) logLastError(L"error geting time string"); @@ -91,9 +91,9 @@ static void setDateTimeFormat(HWND hwnd) if (SendMessageW(hwnd, DTM_SETFORMAT, 0, (LPARAM) datetime) == 0) logLastError(L"error applying format string to date/time picker"); - uiFree(datetime); - uiFree(time); - uiFree(date); + uiprivFree(datetime); + uiprivFree(time); + uiprivFree(date); } // control implementation diff --git a/windows/debug.cpp b/windows/debug.cpp index cfafffdc..9bfbbf0f 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -26,7 +26,7 @@ HRESULT _logLastError(debugargs, const WCHAR *s) if (useFormatted) LocalFree(formatted); // ignore error printDebug(msg); - uiFree(msg); + uiprivFree(msg); DebugBreak(); SetLastError(le); @@ -53,7 +53,7 @@ HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr) if (useFormatted) LocalFree(formatted); // ignore error printDebug(msg); - uiFree(msg); + uiprivFree(msg); DebugBreak(); return hr; @@ -71,14 +71,14 @@ void realbug(const char *file, const char *line, const char *func, const char *p va_end(ap2); n++; // terminating '\0' - msg = (char *) uiAlloc(n * sizeof (char), "char[]"); + msg = (char *) uiprivAlloc(n * sizeof (char), "char[]"); // includes terminating '\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx vsprintf_s(msg, n, format, ap); final = strf(L"[libui] %hs:%hs:%hs() %hs%hs\n", file, line, func, prefix, msg); - uiFree(msg); + uiprivFree(msg); printDebug(final); - uiFree(final); + uiprivFree(final); DebugBreak(); } diff --git a/windows/draw.cpp b/windows/draw.cpp index 5f4d29f1..2fc9eed9 100644 --- a/windows/draw.cpp +++ b/windows/draw.cpp @@ -107,7 +107,7 @@ uiDrawContext *newContext(ID2D1RenderTarget *rt) { uiDrawContext *c; - c = uiNew(uiDrawContext); + c = uiprivNew(uiDrawContext); c->rt = rt; c->states = new std::vector; resetTarget(c->rt); @@ -122,7 +122,7 @@ void freeContext(uiDrawContext *c) // TODO do this on other platforms userbug("You did not balance uiDrawSave() and uiDrawRestore() calls."); delete c->states; - uiFree(c); + uiprivFree(c); } static ID2D1Brush *makeSolidBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props) @@ -152,7 +152,7 @@ static ID2D1GradientStopCollection *mkstops(uiDrawBrush *b, ID2D1RenderTarget *r size_t i; HRESULT hr; - stops = (D2D1_GRADIENT_STOP *) uiAlloc(b->NumStops * sizeof (D2D1_GRADIENT_STOP), "D2D1_GRADIENT_STOP[]"); + stops = (D2D1_GRADIENT_STOP *) uiprivAlloc(b->NumStops * sizeof (D2D1_GRADIENT_STOP), "D2D1_GRADIENT_STOP[]"); for (i = 0; i < b->NumStops; i++) { stops[i].position = b->Stops[i].Pos; stops[i].color.r = b->Stops[i].R; @@ -170,7 +170,7 @@ static ID2D1GradientStopCollection *mkstops(uiDrawBrush *b, ID2D1RenderTarget *r if (hr != S_OK) logHRESULT(L"error creating stop collection", hr); - uiFree(stops); + uiprivFree(stops); return s; } @@ -365,7 +365,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *p, uiDrawBrush *b, uiDrawStrokeP // TODO be sure to formally document this if (sp->NumDashes != 0) { dsp.dashStyle = D2D1_DASH_STYLE_CUSTOM; - dashes = (FLOAT *) uiAlloc(sp->NumDashes * sizeof (FLOAT), "FLOAT[]"); + dashes = (FLOAT *) uiprivAlloc(sp->NumDashes * sizeof (FLOAT), "FLOAT[]"); for (i = 0; i < sp->NumDashes; i++) dashes[i] = sp->Dashes[i] / sp->Thickness; } @@ -378,7 +378,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *p, uiDrawBrush *b, uiDrawStrokeP if (hr != S_OK) logHRESULT(L"error creating stroke style", hr); if (sp->NumDashes != 0) - uiFree(dashes); + uiprivFree(dashes); cliplayer = applyClip(c); c->rt->DrawGeometry( diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index 045d11c0..8baa75a7 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -17,7 +17,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode fillmode) uiDrawPath *p; HRESULT hr; - p = uiNew(uiDrawPath); + p = uiprivNew(uiDrawPath); hr = d2dfactory->CreatePathGeometry(&(p->path)); if (hr != S_OK) logHRESULT(L"error creating path", hr); @@ -43,7 +43,7 @@ void uiDrawFreePath(uiDrawPath *p) // TODO close sink first? p->sink->Release(); p->path->Release(); - uiFree(p); + uiprivFree(p); } void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 85accab1..ec2ae152 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -38,7 +38,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) FLOAT maxWidth; HRESULT hr; - tl = uiNew(uiDrawTextLayout); + tl = uiprivNew(uiDrawTextLayout); wDefaultFamily = toUTF16(p->DefaultFont->Family); hr = dwfactory->CreateTextFormat( @@ -51,7 +51,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) // TODO use the current locale? L"", &(tl->format)); - uiFree(wDefaultFamily); + uiprivFree(wDefaultFamily); if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); hr = tl->format->SetTextAlignment(dwriteAligns[p->Align]); @@ -95,14 +95,14 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - uiFree(tl->u16tou8); - uiFree(tl->u8tou16); + uiprivFree(tl->u16tou8); + uiprivFree(tl->u8tou16); for (auto p : *(tl->backgroundParams)) uiprivFree(p); delete tl->backgroundParams; tl->layout->Release(); tl->format->Release(); - uiFree(tl); + uiprivFree(tl); } // TODO make this shared code somehow diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 24a4aa3a..4d6b6741 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -81,7 +81,7 @@ WCHAR *uiprivFontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStr if (hr != S_OK) logHRESULT(L"error getting length of font name", hr); // GetStringLength() does not include the null terminator, but GetString() does - wname = (WCHAR *) uiAlloc((length + 1) * sizeof (WCHAR), "WCHAR[]"); + wname = (WCHAR *) uiprivAlloc((length + 1) * sizeof (WCHAR), "WCHAR[]"); hr = names->GetString(index, wname, length + 1); if (hr != S_OK) logHRESULT(L"error getting font name", hr); diff --git a/windows/editablecombo.cpp b/windows/editablecombo.cpp index 9e1fdbfb..138618d7 100644 --- a/windows/editablecombo.cpp +++ b/windows/editablecombo.cpp @@ -76,7 +76,7 @@ void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text) logLastError(L"error appending item to uiEditableCombobox"); else if (res == (LRESULT) CB_ERRSPACE) logLastError(L"memory exhausted appending item to uiEditableCombobox"); - uiFree(wtext); + uiprivFree(wtext); } char *uiEditableComboboxText(uiEditableCombobox *c) diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index 2d10a1ae..ea02a416 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -121,7 +121,7 @@ static WCHAR *cbGetItemText(HWND cb, WPARAM item) len = SendMessageW(cb, CB_GETLBTEXTLEN, item, 0); if (len == (LRESULT) CB_ERR) logLastError(L"error getting item text length from combobox"); - text = (WCHAR *) uiAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); + text = (WCHAR *) uiprivAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); if (SendMessageW(cb, CB_GETLBTEXT, item, (LPARAM) text) != len) logLastError(L"error getting item text from combobox"); return text; @@ -138,7 +138,7 @@ static BOOL cbTypeToSelect(HWND cb, LRESULT *posOut, BOOL restoreAfter) text = windowText(cb); pos = SendMessageW(cb, CB_FINDSTRINGEXACT, (WPARAM) (-1), (LPARAM) text); if (pos == (LRESULT) CB_ERR) { - uiFree(text); + uiprivFree(text); return FALSE; } cbSetCurSel(cb, (WPARAM) pos); @@ -147,7 +147,7 @@ static BOOL cbTypeToSelect(HWND cb, LRESULT *posOut, BOOL restoreAfter) if (restoreAfter) if (SendMessageW(cb, WM_SETTEXT, 0, (LPARAM) text) != (LRESULT) TRUE) logLastError(L"error restoring old combobox text"); - uiFree(text); + uiprivFree(text); // and restore the selection like above // TODO isn't there a 32-bit version of this if (SendMessageW(cb, CB_SETEDITSEL, 0, MAKELPARAM(selStart, selEnd)) != (LRESULT) TRUE) @@ -254,7 +254,7 @@ static void familyChanged(struct fontDialog *f) logHRESULT(L"error getting font for filling styles box", hr); label = fontStyleName(f->fc, font); pos = cbAddString(f->styleCombobox, label); - uiFree(label); + uiprivFree(label); cbSetItemData(f->styleCombobox, (WPARAM) pos, (LPARAM) font); if (font->GetWeight() == weight && font->GetStyle() == style && @@ -386,7 +386,7 @@ static void fontDialogDrawSampleText(struct fontDialog *f, ID2D1RenderTarget *rt &format); if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); - uiFree(family); + uiprivFree(family); rect.left = 0; rect.top = 0; @@ -402,7 +402,7 @@ static void fontDialogDrawSampleText(struct fontDialog *f, ID2D1RenderTarget *rt format->Release(); if (exists) - uiFree(sample); + uiprivFree(sample); black->Release(); } @@ -466,7 +466,7 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) HWND samplePlacement; HRESULT hr; - f = uiNew(struct fontDialog); + f = uiprivNew(struct fontDialog); f->hwnd = hwnd; f->params = (struct fontDialogParams *) lParam; @@ -482,7 +482,7 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) logHRESULT(L"error getting font family", hr); wname = uiprivFontCollectionFamilyName(f->fc, family); pos = cbAddString(f->familyCombobox, wname); - uiFree(wname); + uiprivFree(wname); cbSetItemData(f->familyCombobox, (WPARAM) pos, (LPARAM) family); } @@ -506,7 +506,7 @@ static void endFontDialog(struct fontDialog *f, INT_PTR code) uiprivFontCollectionFree(f->fc); if (EndDialog(f->hwnd, code) == 0) logLastError(L"error ending font dialog"); - uiFree(f); + uiprivFree(f); } static INT_PTR tryFinishDialog(struct fontDialog *f, WPARAM wParam) @@ -681,7 +681,7 @@ WCHAR *uiprivFontDialogParamsToString(struct fontDialogParams *params) WCHAR *text; // TODO dynamically allocate - text = (WCHAR *) uiAlloc(512 * sizeof (WCHAR), "WCHAR[]"); + text = (WCHAR *) uiprivAlloc(512 * sizeof (WCHAR), "WCHAR[]"); _snwprintf(text, 512, L"%s %s %g", params->familyName, params->styleName, diff --git a/windows/form.cpp b/windows/form.cpp index febcc693..ed194671 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -266,7 +266,7 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) SS_LEFT | SS_NOPREFIX, hInstance, NULL, TRUE); - uiFree(wlabel); + uiprivFree(wlabel); uiWindowsEnsureSetParentHWND(fc.label, f->hwnd); fc.stretchy = stretchy; uiControlSetParent(fc.c, uiControl(f)); diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 63e4882f..266cf97f 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -17,7 +17,7 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) WCHAR *str; size_t *pPTG, *pGTP; - g = uiNew(struct graphemes); + g = uiprivNew(struct graphemes); g->len = 0; str = (WCHAR *) s; @@ -27,8 +27,8 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) // no need to worry about surrogates if we're just counting } - g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->pointsToGraphemes = (size_t *) uiprivAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiprivAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); pPTG = g->pointsToGraphemes; pGTP = g->graphemesToPoints; diff --git a/windows/grid.cpp b/windows/grid.cpp index c63cd1e4..61e78543 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -418,7 +418,7 @@ static void uiGridDestroy(uiControl *c) for (struct gridChild *gc : *(g->children)) { uiControlSetParent(gc->c, NULL); uiControlDestroy(gc->c); - uiFree(gc); + uiprivFree(gc); } delete g->indexof; delete g->children; @@ -565,7 +565,7 @@ static struct gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand userbug("You cannot have a negative xspan in a uiGrid cell."); if (yspan < 0) userbug("You cannot have a negative yspan in a uiGrid cell."); - gc = uiNew(struct gridChild); + gc = uiprivNew(struct gridChild); gc->c = c; gc->xspan = xspan; gc->yspan = yspan; diff --git a/windows/group.cpp b/windows/group.cpp index 8824c5a4..1a2cc6ed 100644 --- a/windows/group.cpp +++ b/windows/group.cpp @@ -208,7 +208,7 @@ uiGroup *uiNewGroup(const char *text) BS_GROUPBOX, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); if (SetWindowSubclass(g->hwnd, groupSubProc, 0, (DWORD_PTR) g) == FALSE) logLastError(L"error subclassing groupbox to handle parent messages"); diff --git a/windows/init.cpp b/windows/init.cpp index 2c25d3d4..24831143 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -28,11 +28,11 @@ static const char *initerr(const char *message, const WCHAR *label, DWORD value) wmessage, value, value, sysmsg); - uiFree(wmessage); + uiprivFree(wmessage); if (hassysmsg) LocalFree(sysmsg); // ignore error out = toUTF8(wout); - uiFree(wout); + uiprivFree(wout); return out + 1; } @@ -157,7 +157,7 @@ void uiUninit(void) void uiFreeInitError(const char *err) { if (*(err - 1) == '-') - uiFree((void *) (err - 1)); + uiprivFree((void *) (err - 1)); } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) diff --git a/windows/label.cpp b/windows/label.cpp index d74b7d18..94a0d88e 100644 --- a/windows/label.cpp +++ b/windows/label.cpp @@ -51,7 +51,7 @@ uiLabel *uiNewLabel(const char *text) SS_LEFTNOWORDWRAP | SS_NOPREFIX, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); return l; } diff --git a/windows/menu.cpp b/windows/menu.cpp index 6112fc13..46405faf 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -115,10 +115,10 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) if (m->len >= m->cap) { m->cap += grow; - m->items = (uiMenuItem **) uiRealloc(m->items, m->cap * sizeof (uiMenuItem *), "uiMenuitem *[]"); + m->items = (uiMenuItem **) uiprivRealloc(m->items, m->cap * sizeof (uiMenuItem *), "uiMenuitem *[]"); } - item = uiNew(uiMenuItem); + item = uiprivNew(uiMenuItem); m->items[m->len] = item; m->len++; @@ -207,10 +207,10 @@ uiMenu *uiNewMenu(const char *name) userbug("You can not create a new menu after menus have been finalized."); if (len >= cap) { cap += grow; - menus = (uiMenu **) uiRealloc(menus, cap * sizeof (uiMenu *), "uiMenu *[]"); + menus = (uiMenu **) uiprivRealloc(menus, cap * sizeof (uiMenu *), "uiMenu *[]"); } - m = uiNew(uiMenu); + m = uiprivNew(uiMenu); menus[len] = m; len++; @@ -237,7 +237,7 @@ static void appendMenuItem(HMENU menu, uiMenuItem *item) if (item->len >= item->cap) { item->cap += grow; - item->hmenus = (HMENU *) uiRealloc(item->hmenus, item->cap * sizeof (HMENU), "HMENU[]"); + item->hmenus = (HMENU *) uiprivRealloc(item->hmenus, item->cap * sizeof (HMENU), "HMENU[]"); } item->hmenus[item->len] = menu; item->len++; @@ -348,22 +348,22 @@ void uninitMenus(void) for (i = 0; i < len; i++) { m = menus[i]; - uiFree(m->name); + uiprivFree(m->name); for (j = 0; j < m->len; j++) { item = m->items[j]; if (item->len != 0) // LONGTERM userbug()? implbug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); if (item->name != NULL) - uiFree(item->name); + uiprivFree(item->name); if (item->hmenus != NULL) - uiFree(item->hmenus); - uiFree(item); + uiprivFree(item->hmenus); + uiprivFree(item); } if (m->items != NULL) - uiFree(m->items); - uiFree(m); + uiprivFree(m->items); + uiprivFree(m); } if (menus != NULL) - uiFree(menus); + uiprivFree(menus); } diff --git a/windows/multilineentry.cpp b/windows/multilineentry.cpp index a32960cb..391f4855 100644 --- a/windows/multilineentry.cpp +++ b/windows/multilineentry.cpp @@ -76,7 +76,7 @@ void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) e->inhibitChanged = TRUE; crlf = LFtoCRLF(text); uiWindowsSetWindowText(e->hwnd, text); - uiFree(crlf); + uiprivFree(crlf); e->inhibitChanged = FALSE; // don't queue the control for resize; entry sizes are independent of their contents } @@ -95,9 +95,9 @@ void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) SendMessageW(e->hwnd, EM_SETSEL, n, n); crlf = LFtoCRLF(text); wtext = toUTF16(crlf); - uiFree(crlf); + uiprivFree(crlf); SendMessageW(e->hwnd, EM_REPLACESEL, FALSE, (LPARAM) wtext); - uiFree(wtext); + uiprivFree(wtext); e->inhibitChanged = FALSE; } diff --git a/windows/radiobuttons.cpp b/windows/radiobuttons.cpp index 29cd2e66..0684a270 100644 --- a/windows/radiobuttons.cpp +++ b/windows/radiobuttons.cpp @@ -140,7 +140,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) BS_RADIOBUTTON | groupTabStop, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); uiWindowsEnsureSetParentHWND(hwnd, r->hwnd); uiWindowsRegisterWM_COMMANDHandler(hwnd, onWM_COMMAND, uiControl(r)); r->hwnds->push_back(hwnd); diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index 8c4b666a..57fb8d64 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -48,10 +48,10 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) // This won't handle leading spaces, but spaces aren't allowed *anyway*. wtext = windowText(s->edit); if (wcscmp(wtext, L"-") == 0) { - uiFree(wtext); + uiprivFree(wtext); return TRUE; } - uiFree(wtext); + uiprivFree(wtext); // value() does the work for us value(s); (*(s->onChanged))(s, s->onChangedData); diff --git a/windows/stddialogs.cpp b/windows/stddialogs.cpp index 89d26bac..c6ac4557 100644 --- a/windows/stddialogs.cpp +++ b/windows/stddialogs.cpp @@ -114,8 +114,8 @@ static void msgbox(HWND parent, const char *title, const char *description, TASK if (hr != S_OK) logHRESULT(L"error showing task dialog", hr); - uiFree(wdescription); - uiFree(wtitle); + uiprivFree(wdescription); + uiprivFree(wtitle); } void uiMsgBox(uiWindow *parent, const char *title, const char *description) diff --git a/windows/tab.cpp b/windows/tab.cpp index 365f5a1f..e7239585 100644 --- a/windows/tab.cpp +++ b/windows/tab.cpp @@ -212,7 +212,7 @@ void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child) item.pszText = wname; if (SendMessageW(t->tabHWND, TCM_INSERTITEM, (WPARAM) n, (LPARAM) (&item)) == (LRESULT) -1) logLastError(L"error adding tab to uiTab"); - uiFree(wname); + uiprivFree(wname); // we need to do this because adding the first tab doesn't send a TCN_SELCHANGE; it just shows the page show = curpage(t); diff --git a/windows/tabpage.cpp b/windows/tabpage.cpp index 77df9a18..dc98fb88 100644 --- a/windows/tabpage.cpp +++ b/windows/tabpage.cpp @@ -83,7 +83,7 @@ struct tabPage *newTabPage(uiControl *child) struct tabPage *tp; HRESULT hr; - tp = uiNew(struct tabPage); + tp = uiprivNew(struct tabPage); // unfortunately this needs to be a proper dialog for EnableThemeDialogTexture() to work; CreateWindowExW() won't suffice if (CreateDialogParamW(hInstance, MAKEINTRESOURCE(rcTabPageDialog), @@ -114,7 +114,7 @@ void tabPageDestroy(struct tabPage *tp) uiWindowsControlSetParentHWND(uiWindowsControl(tp->child), NULL); // don't call EndDialog(); that's for the DialogBox() family of functions instead of CreateDialog() uiWindowsEnsureDestroyWindow(tp->hwnd); - uiFree(tp); + uiprivFree(tp); } void tabPageMinimumSize(struct tabPage *tp, int *width, int *height) diff --git a/windows/text.cpp b/windows/text.cpp index e3a23570..eafbe714 100644 --- a/windows/text.cpp +++ b/windows/text.cpp @@ -10,7 +10,7 @@ WCHAR *windowTextAndLen(HWND hwnd, LRESULT *len) if (len != NULL) *len = n; // WM_GETTEXTLENGTH does not include the null terminator - text = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); + text = (WCHAR *) uiprivAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); // note the comparison: the size includes the null terminator, but the return does not if (GetWindowTextW(hwnd, text, n + 1) != n) { logLastError(L"error getting window text"); @@ -35,7 +35,7 @@ void setWindowText(HWND hwnd, WCHAR *wtext) void uiFreeText(char *text) { - uiFree(text); + uiprivFree(text); } int uiWindowsWindowTextWidth(HWND hwnd) @@ -78,11 +78,11 @@ int uiWindowsWindowTextWidth(HWND hwnd) if (ReleaseDC(hwnd, dc) == 0) logLastError(L"error releasing DC"); - uiFree(text); + uiprivFree(text); return size.cx; noTextOrError: - uiFree(text); + uiprivFree(text); return 0; } @@ -93,7 +93,7 @@ char *uiWindowsWindowText(HWND hwnd) wtext = windowText(hwnd); text = toUTF8(wtext); - uiFree(wtext); + uiprivFree(wtext); return text; } @@ -103,7 +103,7 @@ void uiWindowsSetWindowText(HWND hwnd, const char *text) wtext = toUTF16(text); setWindowText(hwnd, wtext); - uiFree(wtext); + uiprivFree(wtext); } int uiprivStricmp(const char *a, const char *b) @@ -114,7 +114,7 @@ int uiprivStricmp(const char *a, const char *b) wa = toUTF16(a); wb = toUTF16(b); ret = _wcsicmp(wa, wb); - uiFree(wb); - uiFree(wa); + uiprivFree(wb); + uiprivFree(wa); return ret; } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 3244b7b4..7fb00efa 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -29,8 +29,8 @@ extern BOOL runWM_HSCROLL(WPARAM wParam, LPARAM lParam, LRESULT *lResult); extern void issueWM_WININICHANGE(WPARAM wParam, LPARAM lParam); // utf16.cpp -#define emptyUTF16() ((WCHAR *) uiAlloc(1 * sizeof (WCHAR), "WCHAR[]")) -#define emptyUTF8() ((char *) uiAlloc(1 * sizeof (char), "char[]")) +#define emptyUTF16() ((WCHAR *) uiprivAlloc(1 * sizeof (WCHAR), "WCHAR[]")) +#define emptyUTF8() ((char *) uiprivAlloc(1 * sizeof (char), "char[]")) extern WCHAR *toUTF16(const char *str); extern char *toUTF8(const WCHAR *wstr); extern WCHAR *utf16dup(const WCHAR *orig); diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 21d2f8a5..6afd0b0e 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -13,7 +13,7 @@ WCHAR *toUTF16(const char *str) if (*str == '\0') // empty string return emptyUTF16(); n = utf8UTF16Count(str, 0); - wstr = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); + wstr = (WCHAR *) uiprivAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); wp = wstr; while (*str) { str = utf8DecodeRune(str, 0, &rune); @@ -33,7 +33,7 @@ char *toUTF8(const WCHAR *wstr) if (*wstr == L'\0') // empty string return emptyUTF8(); n = utf16RuneCount(wstr, 0); - str = (char *) uiAlloc((n + 1) * sizeof (char), "char[]"); + str = (char *) uiprivAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { wstr = utf16DecodeRune(wstr, 0, &rune); @@ -49,7 +49,7 @@ WCHAR *utf16dup(const WCHAR *orig) size_t len; len = wcslen(orig); - out = (WCHAR *) uiAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); + out = (WCHAR *) uiprivAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); wcscpy_s(out, len + 1, orig); return out; } @@ -79,7 +79,7 @@ WCHAR *vstrf(const WCHAR *format, va_list ap) va_end(ap2); n++; // terminating L'\0' - buf = (WCHAR *) uiAlloc(n * sizeof (WCHAR), "WCHAR[]"); + buf = (WCHAR *) uiprivAlloc(n * sizeof (WCHAR), "WCHAR[]"); // includes terminating L'\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx vswprintf_s(buf, n, format, ap); @@ -97,7 +97,7 @@ char *LFtoCRLF(const char *lfonly) char *out; len = strlen(lfonly); - crlf = (char *) uiAlloc((len * 2 + 1) * sizeof (char), "char[]"); + crlf = (char *) uiprivAlloc((len * 2 + 1) * sizeof (char), "char[]"); out = crlf; for (i = 0; i < len; i++) { if (*lfonly == '\n') diff --git a/windows/window.cpp b/windows/window.cpp index 34baf545..2ea5b7ce 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -480,7 +480,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) NULL, NULL, hInstance, w); if (w->hwnd == NULL) logLastError(L"error creating window"); - uiFree(wtitle); + uiprivFree(wtitle); if (hasMenubar) { w->menubar = makeMenubar(); From f93973d3cbe4ecb1cd3f484227dee74c742b791f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 21:46:08 -0400 Subject: [PATCH 472/487] Migrated implbug() and userbug() to uipriv forms. --- common/OLD_uipriv.h | 12 ------------ common/control.c | 8 ++++---- common/debug.c | 8 ++++---- common/uipriv.h | 24 +++++++++++++++--------- common/userbugs.c | 2 +- darwin/alloc.m | 4 ++-- darwin/area.m | 4 ++-- darwin/box.m | 2 +- darwin/debug.m | 2 +- darwin/draw.m | 22 +++++++++++----------- darwin/form.m | 2 +- darwin/grid.m | 8 ++++---- darwin/main.m | 7 +++---- darwin/map.m | 2 +- darwin/menu.m | 12 ++++++------ darwin/progressbar.m | 2 +- unix/alloc.c | 8 ++++---- unix/area.c | 6 +++--- unix/debug.c | 2 +- unix/draw.c | 2 +- unix/drawpath.c | 4 ++-- unix/menu.c | 18 +++++++++--------- unix/progressbar.c | 2 +- windows/alloc.cpp | 4 ++-- windows/datetimepicker.cpp | 2 +- windows/debug.cpp | 2 +- windows/draw.cpp | 4 ++-- windows/drawpath.cpp | 2 +- windows/events.cpp | 16 ++++++++-------- windows/grid.cpp | 4 ++-- windows/menu.cpp | 22 +++++++++++----------- windows/progressbar.cpp | 2 +- 32 files changed, 107 insertions(+), 114 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 64d82ad1..ac8ec0cc 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,16 +1,4 @@ -// ugh, this was only introduced in MSVC 2015... -#ifdef _MSC_VER -#define __func__ __FUNCTION__ -#endif -extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); -#define _ns2(s) #s -#define _ns(s) _ns2(s) -extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); -#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) -extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); -#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) - // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/common/control.c b/common/control.c index e46e322c..3b5b8286 100644 --- a/common/control.c +++ b/common/control.c @@ -73,7 +73,7 @@ uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const c void uiFreeControl(uiControl *c) { if (uiControlParent(c) != NULL) - userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); + uiprivUserBug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); uiprivFree(c); } @@ -82,12 +82,12 @@ void uiControlVerifySetParent(uiControl *c, uiControl *parent) uiControl *curParent; if (uiControlToplevel(c)) - userbug("You cannot give a toplevel uiControl a parent. (control: %p)", c); + uiprivUserBug("You cannot give a toplevel uiControl a parent. (control: %p)", c); curParent = uiControlParent(c); if (parent != NULL && curParent != NULL) - userbug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent); + uiprivUserBug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent); if (parent == NULL && curParent == NULL) - implbug("attempt to double unparent uiControl %p", c); + uiprivImplBug("attempt to double unparent uiControl %p", c); } int uiControlEnabledToUser(uiControl *c) diff --git a/common/debug.c b/common/debug.c index 97280b47..aa24e29f 100644 --- a/common/debug.c +++ b/common/debug.c @@ -2,20 +2,20 @@ #include "../ui.h" #include "uipriv.h" -void _implbug(const char *file, const char *line, const char *func, const char *format, ...) +void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...) { va_list ap; va_start(ap, format); - realbug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); + uiprivRealBug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); va_end(ap); } -void _userbug(const char *file, const char *line, const char *func, const char *format, ...) +void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...) { va_list ap; va_start(ap, format); - realbug(file, line, func, "You have a bug: ", format, ap); + uiprivRealBug(file, line, func, "You have a bug: ", format, ap); va_end(ap); } diff --git a/common/uipriv.h b/common/uipriv.h index 69062c87..530a9794 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -8,24 +8,30 @@ extern "C" { #endif +// OS-specific init.* or main.* files extern uiInitOptions uiprivOptions; +// OS-specific alloc.* files extern void *uiprivAlloc(size_t, const char *); #define uiprivNew(T) ((T *) uiprivAlloc(sizeof (T), #T)) extern void *uiprivRealloc(void *, size_t, const char *); extern void uiprivFree(void *); -// ugh, this was only introduced in MSVC 2015... +// debug.c and OS-specific debug.* files +// TODO get rid of this mess... +// ugh, __func__ was only introduced in MSVC 2015... #ifdef _MSC_VER -#define __func__ __FUNCTION__ +#define uiprivMacro__func__ __FUNCTION__ +#else +#define uiprivMacro__func__ __func__ #endif -extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); -#define _ns2(s) #s -#define _ns(s) _ns2(s) -extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); -#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) -extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); -#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) +extern void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); +#define uiprivMacro_ns2(s) #s +#define uiprivMacro_ns(s) uiprivMacro_ns2(s) +extern void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...); +#define uiprivImplBug(...) uiprivDoImplBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) +extern void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...); +#define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/common/userbugs.c b/common/userbugs.c index 0a85874c..09cc703d 100644 --- a/common/userbugs.c +++ b/common/userbugs.c @@ -4,5 +4,5 @@ void uiUserBugCannotSetParentOnToplevel(const char *type) { - userbug("You cannot make a %s a child of another uiControl,", type); + uiprivUserBug("You cannot make a %s a child of another uiControl,", type); } diff --git a/darwin/alloc.m b/darwin/alloc.m index 0012c853..fbafc153 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -37,7 +37,7 @@ void uninitAlloc(void) ptr = [v pointerValue]; [str appendString:[NSString stringWithFormat:@"%p %s\n", ptr, *TYPE(ptr)]]; } - userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]); + uiprivUserBug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]); [str release]; } @@ -82,7 +82,7 @@ void *uiprivRealloc(void *p, size_t new, const char *type) void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiprivFree(NULL)"); + uiprivImplBug("attempt to uiprivFree(NULL)"); p = BASE(p); free(p); [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/darwin/area.m b/darwin/area.m index 23162e6c..1442479a 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -390,7 +390,7 @@ int sendAreaEvents(NSEvent *e) void uiAreaSetSize(uiArea *a, int width, int height) { if (!a->scrolling) - userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); + uiprivUserBug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); [a->area setScrollingSize:NSMakeSize(width, height)]; } @@ -402,7 +402,7 @@ void uiAreaQueueRedrawAll(uiArea *a) void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) { if (!a->scrolling) - userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (area: %p)", a); + uiprivUserBug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (area: %p)", a); [a->area scrollRectToVisible:NSMakeRect(x, y, width, height)]; // don't worry about the return value; it just says whether scrolling was needed } diff --git a/darwin/box.m b/darwin/box.m index 18d536d5..6a1941ea 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -428,7 +428,7 @@ void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) // LONGTERM on other platforms // or at leat allow this and implicitly turn it into a spacer if (c == NULL) - userbug("You cannot add NULL to a uiBox."); + uiprivUserBug("You cannot add NULL to a uiBox."); [b->view append:c stretchy:stretchy]; } diff --git a/darwin/debug.m b/darwin/debug.m index c91c6a73..aff66e0d 100644 --- a/darwin/debug.m +++ b/darwin/debug.m @@ -3,7 +3,7 @@ // LONGTERM don't halt on release builds -void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { NSMutableString *str; NSString *formatted; diff --git a/darwin/draw.m b/darwin/draw.m index b52b5a57..7ca854d2 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -27,7 +27,7 @@ void uiDrawFreePath(uiDrawPath *p) void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) { if (p->ended) - userbug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p); + uiprivUserBug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathMoveToPoint(p->path, NULL, x, y); } @@ -37,7 +37,7 @@ void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, d double startx, starty; if (p->ended) - userbug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p); + uiprivUserBug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p); sinStart = sin(startAngle); cosStart = cos(startAngle); startx = xCenter + radius * cosStart; @@ -50,7 +50,7 @@ void uiDrawPathLineTo(uiDrawPath *p, double x, double y) { // TODO refine this to require being in a path if (p->ended) - implbug("attempt to add line to ended path in uiDrawPathLineTo()"); + uiprivImplBug("attempt to add line to ended path in uiDrawPathLineTo()"); CGPathAddLineToPoint(p->path, NULL, x, y); } @@ -60,7 +60,7 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu // TODO likewise if (p->ended) - implbug("attempt to add arc to ended path in uiDrawPathArcTo()"); + uiprivImplBug("attempt to add arc to ended path in uiDrawPathArcTo()"); if (sweep > 2 * uiPi) sweep = 2 * uiPi; cw = false; @@ -77,7 +77,7 @@ void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, doubl { // TODO likewise if (p->ended) - implbug("attempt to add bezier to ended path in uiDrawPathBezierTo()"); + uiprivImplBug("attempt to add bezier to ended path in uiDrawPathBezierTo()"); CGPathAddCurveToPoint(p->path, NULL, c1x, c1y, c2x, c2y, @@ -88,14 +88,14 @@ void uiDrawPathCloseFigure(uiDrawPath *p) { // TODO likewise if (p->ended) - implbug("attempt to close figure of ended path in uiDrawPathCloseFigure()"); + uiprivImplBug("attempt to close figure of ended path in uiDrawPathCloseFigure()"); CGPathCloseSubpath(p->path); } void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height) { if (p->ended) - userbug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p); + uiprivUserBug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathAddRect(p->path, NULL, CGRectMake(x, y, width, height)); } @@ -132,7 +132,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro uiDrawPath p2; if (!path->ended) - userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); + uiprivUserBug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); switch (p->Cap) { case uiDrawLineCapFlat: @@ -280,7 +280,7 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) { if (!path->ended) - userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); + uiprivUserBug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (b->Type) { case uiDrawBrushTypeSolid: @@ -294,7 +294,7 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) // TODO return; } - userbug("Unknown brush type %d passed to uiDrawFill().", b->Type); + uiprivUserBug("Unknown brush type %d passed to uiDrawFill().", b->Type); } static void m2c(uiDrawMatrix *m, CGAffineTransform *c) @@ -425,7 +425,7 @@ void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m) void uiDrawClip(uiDrawContext *c, uiDrawPath *path) { if (!path->ended) - userbug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path); + uiprivUserBug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (path->fillMode) { case uiDrawFillModeWinding: diff --git a/darwin/form.m b/darwin/form.m index 7cdb965a..613818a9 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -530,7 +530,7 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) // LONGTERM on other platforms // or at leat allow this and implicitly turn it into a spacer if (c == NULL) - userbug("You cannot add NULL to a uiForm."); + uiprivUserBug("You cannot add NULL to a uiForm."); [f->view append:toNSString(label) c:c stretchy:stretchy]; } diff --git a/darwin/grid.m b/darwin/grid.m index fc98cc9f..218a5f63 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -574,7 +574,7 @@ struct uiGrid { break; } if (!found) - userbug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g); + uiprivUserBug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g); switch (at) { case uiAtLeading: @@ -742,9 +742,9 @@ static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAli gridChild *gc; if (xspan < 0) - userbug("You cannot have a negative xspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative xspan in a uiGrid cell."); if (yspan < 0) - userbug("You cannot have a negative yspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative yspan in a uiGrid cell."); gc = [gridChild new]; gc.xspan = xspan; gc.yspan = yspan; @@ -763,7 +763,7 @@ void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int ysp // LONGTERM on other platforms // or at leat allow this and implicitly turn it into a spacer if (c == NULL) - userbug("You cannot add NULL to a uiGrid."); + uiprivUserBug("You cannot add NULL to a uiGrid."); gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g); gc.left = left; gc.top = top; diff --git a/darwin/main.m b/darwin/main.m index 1bc6bfc3..f3392ce6 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -57,7 +57,7 @@ static BOOL stepsIsRunning; NSEvent *e; if (!canQuit) - implbug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs"); + uiprivImplBug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs"); [realNSApp() stop:realNSApp()]; // stop: won't register until another event has passed; let's synthesize one @@ -139,9 +139,8 @@ const char *uiInit(uiInitOptions *o) void uiUninit(void) { - if (!globalPool) { - userbug("You must call uiInit() first!"); - } + if (!globalPool) + uiprivUserBug("You must call uiInit() first!"); [globalPool release]; @autoreleasepool { diff --git a/darwin/map.m b/darwin/map.m index 4eaa057d..190218a1 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -22,7 +22,7 @@ struct mapTable *newMap(void) void mapDestroy(struct mapTable *m) { if ([m->m count] != 0) - implbug("attempt to destroy map with items inside"); + uiprivImplBug("attempt to destroy map with items inside"); [m->m release]; uiprivFree(m); } diff --git a/darwin/menu.m b/darwin/menu.m index 11a98e63..ca6cce4b 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -80,17 +80,17 @@ static void mapItemReleaser(void *key, void *value) switch (smi->type) { case typeQuit: if (self->hasQuit) - userbug("You can't have multiple Quit menu items in one program."); + uiprivUserBug("You can't have multiple Quit menu items in one program."); self->hasQuit = YES; break; case typePreferences: if (self->hasPreferences) - userbug("You can't have multiple Preferences menu items in one program."); + uiprivUserBug("You can't have multiple Preferences menu items in one program."); self->hasPreferences = YES; break; case typeAbout: if (self->hasAbout) - userbug("You can't have multiple About menu items in one program."); + uiprivUserBug("You can't have multiple About menu items in one program."); self->hasAbout = YES; break; } @@ -212,7 +212,7 @@ void uiMenuItemDisable(uiMenuItem *item) void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (item->type == typeQuit) - userbug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); + uiprivUserBug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); item->onClicked = f; item->onClickedData = data; } @@ -239,7 +239,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - userbug("You can't create a new menu item after menus have been finalized."); + uiprivUserBug("You can't create a new menu item after menus have been finalized."); item = uiprivNew(uiMenuItem); @@ -315,7 +315,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - userbug("You can't create a new menu after menus have been finalized."); + uiprivUserBug("You can't create a new menu after menus have been finalized."); if (menus == nil) menus = [NSMutableArray new]; diff --git a/darwin/progressbar.m b/darwin/progressbar.m index b5382281..1f5390ff 100644 --- a/darwin/progressbar.m +++ b/darwin/progressbar.m @@ -48,7 +48,7 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) } if (value < 0 || value > 100) - userbug("Value %d out of range for a uiProgressBar.", value); + uiprivUserBug("Value %d out of range for a uiProgressBar.", value); // on 10.8 there's an animation when the progress bar increases, just like with Aero if (value == 100) { diff --git a/unix/alloc.c b/unix/alloc.c index 98eb0e32..2fdd2052 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -39,7 +39,7 @@ void uninitAlloc(void) return; } g_ptr_array_foreach(allocations, uninitComplain, &str); - userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", str); + uiprivUserBug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", str); g_free(str); } @@ -68,7 +68,7 @@ void *uiprivRealloc(void *p, size_t new, const char *type) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiprivRealloc()", p); + uiprivImplBug("%p not found in allocations array in uiprivRealloc()", p); g_ptr_array_add(allocations, out); return DATA(out); } @@ -76,9 +76,9 @@ void *uiprivRealloc(void *p, size_t new, const char *type) void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiprivFree(NULL)"); + uiprivImplBug("attempt to uiprivFree(NULL)"); p = BASE(p); g_free(p); if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiprivFree()", p); + uiprivImplBug("%p not found in allocations array in uiprivFree()", p); } diff --git a/unix/area.c b/unix/area.c index 24cd9513..abf868ca 100644 --- a/unix/area.c +++ b/unix/area.c @@ -499,7 +499,7 @@ uiUnixControlAllDefaults(uiArea) void uiAreaSetSize(uiArea *a, int width, int height) { if (!a->scrolling) - userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); + uiprivUserBug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); a->scrollWidth = width; a->scrollHeight = height; gtk_widget_queue_resize(a->areaWidget); @@ -521,7 +521,7 @@ void uiAreaBeginUserWindowMove(uiArea *a) GtkWidget *toplevel; if (a->dragevent == NULL) - userbug("cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0"); + uiprivUserBug("cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0"); // TODO don't we have a libui function for this? did I scrap it? // TODO widget or areaWidget? toplevel = gtk_widget_get_toplevel(a->widget); @@ -561,7 +561,7 @@ void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) GtkWidget *toplevel; if (a->dragevent == NULL) - userbug("cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0"); + uiprivUserBug("cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0"); // TODO don't we have a libui function for this? did I scrap it? // TODO widget or areaWidget? toplevel = gtk_widget_get_toplevel(a->widget); diff --git a/unix/debug.c b/unix/debug.c index c948db62..fd97c9ed 100644 --- a/unix/debug.c +++ b/unix/debug.c @@ -3,7 +3,7 @@ // LONGTERM don't halt on release builds -void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { char *a, *b; diff --git a/unix/draw.c b/unix/draw.c index 1eacee33..15abb611 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -39,7 +39,7 @@ static cairo_pattern_t *mkbrush(uiDrawBrush *b) // case uiDrawBrushTypeImage: } if (cairo_pattern_status(pat) != CAIRO_STATUS_SUCCESS) - implbug("error creating pattern in mkbrush(): %s", + uiprivImplBug("error creating pattern in mkbrush(): %s", cairo_status_to_string(cairo_pattern_status(pat))); switch (b->Type) { case uiDrawBrushTypeLinearGradient: diff --git a/unix/drawpath.c b/unix/drawpath.c index 384743dc..28eeb981 100644 --- a/unix/drawpath.c +++ b/unix/drawpath.c @@ -43,7 +43,7 @@ void uiDrawFreePath(uiDrawPath *p) static void add(uiDrawPath *p, struct piece *piece) { if (p->ended) - userbug("You cannot modify a uiDrawPath that has been ended. (path: %p)", p); + uiprivUserBug("You cannot modify a uiDrawPath that has been ended. (path: %p)", p); g_array_append_vals(p->pieces, piece, 1); } @@ -145,7 +145,7 @@ void runPath(uiDrawPath *p, cairo_t *cr) void (*arc)(cairo_t *, double, double, double, double, double); if (!p->ended) - userbug("You cannot draw with a uiDrawPath that has not been ended. (path: %p)", p); + uiprivUserBug("You cannot draw with a uiDrawPath that has not been ended. (path: %p)", p); cairo_new_path(cr); for (i = 0; i < p->pieces->len; i++) { piece = &g_array_index(p->pieces, struct piece, i); diff --git a/unix/menu.c b/unix/menu.c index 1d950c5f..3d02e939 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -109,7 +109,7 @@ void uiMenuItemDisable(uiMenuItem *item) void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (item->type == typeQuit) - userbug("You cannot call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); + uiprivUserBug("You cannot call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); item->onClicked = f; item->onClickedData = data; } @@ -135,7 +135,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - userbug("You cannot create a new menu item after menus have been finalized."); + uiprivUserBug("You cannot create a new menu item after menus have been finalized."); item = uiprivNew(uiMenuItem); @@ -196,7 +196,7 @@ uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name) uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) { if (hasQuit) - userbug("You cannot have multiple Quit menu items in the same program."); + uiprivUserBug("You cannot have multiple Quit menu items in the same program."); hasQuit = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeQuit, NULL); @@ -205,7 +205,7 @@ uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) { if (hasPreferences) - userbug("You cannot have multiple Preferences menu items in the same program."); + uiprivUserBug("You cannot have multiple Preferences menu items in the same program."); hasPreferences = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typePreferences, NULL); @@ -214,7 +214,7 @@ uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) { if (hasAbout) - userbug("You cannot have multiple About menu items in the same program."); + uiprivUserBug("You cannot have multiple About menu items in the same program."); hasAbout = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeAbout, NULL); @@ -230,7 +230,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - userbug("You cannot create a new menu after menus have been finalized."); + uiprivUserBug("You cannot create a new menu after menus have been finalized."); if (menus == NULL) menus = g_array_new(FALSE, TRUE, sizeof (uiMenu *)); @@ -308,7 +308,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data) item = g_array_index(fmi->items, uiMenuItem *, fmi->i); w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget); if (g_hash_table_remove(item->windows, widget) == FALSE) - implbug("GtkMenuItem %p not in menu item's item/window map", widget); + uiprivImplBug("GtkMenuItem %p not in menu item's item/window map", widget); uiprivFree(w); fmi->i++; } @@ -353,8 +353,8 @@ void uninitMenus(void) for (j = 0; j < m->items->len; j++) { item = g_array_index(m->items, uiMenuItem *, j); if (g_hash_table_size(item->windows) != 0) - // TODO is this really a userbug()? - implbug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); + // TODO is this really a uiprivUserBug()? + uiprivImplBug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); g_free(item->name); g_hash_table_destroy(item->windows); uiprivFree(item); diff --git a/unix/progressbar.c b/unix/progressbar.c index 9b543b04..b3681a6f 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -53,7 +53,7 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) } if (value < 0 || value > 100) - userbug("Value %d is out of range for a uiProgressBar.", value); + uiprivUserBug("Value %d is out of range for a uiProgressBar.", value); gtk_progress_bar_set_fraction(p->pbar, ((gdouble) value) / 100); } diff --git a/windows/alloc.cpp b/windows/alloc.cpp index 244b2380..321cca03 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -22,7 +22,7 @@ void uninitAlloc(void) // note the void * cast; otherwise it'll be treated as a string oss << (void *) (alloc.first) << " " << types[alloc.second] << "\n"; ossstr = oss.str(); - userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", ossstr.c_str()); + uiprivUserBug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", ossstr.c_str()); } #define rawBytes(pa) (&((*pa)[0])) @@ -57,7 +57,7 @@ void uiprivFree(void *_p) uint8_t *p = (uint8_t *) _p; if (p == NULL) - implbug("attempt to uiprivFree(NULL)"); + uiprivImplBug("attempt to uiprivFree(NULL)"); types.erase(heap[p]); delete heap[p]; heap.erase(p); diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 342eb256..6784fec2 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -44,7 +44,7 @@ static WCHAR *expandYear(WCHAR *dts, int n) if (*p == L'\'') break; if (*p == L'\0') - implbug("unterminated quote in system-provided locale date string in expandYear()"); + uiprivImplBug("unterminated quote in system-provided locale date string in expandYear()"); *q++ = *p; } // and fall through to copy the closing quote diff --git a/windows/debug.cpp b/windows/debug.cpp index 9bfbbf0f..bd512743 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -59,7 +59,7 @@ HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr) return hr; } -void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { va_list ap2; char *msg; diff --git a/windows/draw.cpp b/windows/draw.cpp index 2fc9eed9..a5e5033a 100644 --- a/windows/draw.cpp +++ b/windows/draw.cpp @@ -120,7 +120,7 @@ void freeContext(uiDrawContext *c) c->currentClip->Release(); if (c->states->size() != 0) // TODO do this on other platforms - userbug("You did not balance uiDrawSave() and uiDrawRestore() calls."); + uiprivUserBug("You did not balance uiDrawSave() and uiDrawRestore() calls."); delete c->states; uiprivFree(c); } @@ -253,7 +253,7 @@ static ID2D1Brush *makeBrush(uiDrawBrush *b, ID2D1RenderTarget *rt) } // TODO do this on all platforms - userbug("Invalid brush type %d given to drawing operation.", b->Type); + uiprivUserBug("Invalid brush type %d given to drawing operation.", b->Type); // TODO dummy brush? return NULL; // make compiler happy } diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index 8baa75a7..34b15466 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -242,6 +242,6 @@ void uiDrawPathEnd(uiDrawPath *p) ID2D1PathGeometry *pathGeometry(uiDrawPath *p) { if (p->sink != NULL) - userbug("You cannot draw with a uiDrawPath that was not ended. (path: %p)", p); + uiprivUserBug("You cannot draw with a uiDrawPath that was not ended. (path: %p)", p); return p->path; } diff --git a/windows/events.cpp b/windows/events.cpp index 45e8d43d..c13d6d00 100644 --- a/windows/events.cpp +++ b/windows/events.cpp @@ -23,7 +23,7 @@ static std::map handlers; void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c) { if (handlers[hwnd].commandHandler != NULL) - implbug("already registered a WM_COMMAND handler to window handle %p", hwnd); + uiprivImplBug("already registered a WM_COMMAND handler to window handle %p", hwnd); handlers[hwnd].commandHandler = handler; handlers[hwnd].c = c; } @@ -31,7 +31,7 @@ void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, NMHDR *, LRESULT *), uiControl *c) { if (handlers[hwnd].notifyHandler != NULL) - implbug("already registered a WM_NOTIFY handler to window handle %p", hwnd); + uiprivImplBug("already registered a WM_NOTIFY handler to window handle %p", hwnd); handlers[hwnd].notifyHandler = handler; handlers[hwnd].c = c; } @@ -39,7 +39,7 @@ void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, H void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c) { if (handlers[hwnd].hscrollHandler != NULL) - implbug("already registered a WM_HSCROLL handler to window handle %p", hwnd); + uiprivImplBug("already registered a WM_HSCROLL handler to window handle %p", hwnd); handlers[hwnd].hscrollHandler = handler; handlers[hwnd].c = c; } @@ -47,21 +47,21 @@ void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, void uiWindowsUnregisterWM_COMMANDHandler(HWND hwnd) { if (handlers[hwnd].commandHandler == NULL) - implbug("window handle %p not registered to receive WM_COMMAND events", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_COMMAND events", hwnd); handlers[hwnd].commandHandler = NULL; } void uiWindowsUnregisterWM_NOTIFYHandler(HWND hwnd) { if (handlers[hwnd].notifyHandler == NULL) - implbug("window handle %p not registered to receive WM_NOTIFY events", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_NOTIFY events", hwnd); handlers[hwnd].notifyHandler = NULL; } void uiWindowsUnregisterWM_HSCROLLHandler(HWND hwnd) { if (handlers[hwnd].hscrollHandler == NULL) - implbug("window handle %p not registered to receive WM_HSCROLL events", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_HSCROLL events", hwnd); handlers[hwnd].hscrollHandler = NULL; } @@ -131,14 +131,14 @@ static std::map wininichanges; void uiWindowsRegisterReceiveWM_WININICHANGE(HWND hwnd) { if (wininichanges[hwnd]) - implbug("window handle %p already subscribed to receive WM_WINICHANGEs", hwnd); + uiprivImplBug("window handle %p already subscribed to receive WM_WINICHANGEs", hwnd); wininichanges[hwnd] = true; } void uiWindowsUnregisterReceiveWM_WININICHANGE(HWND hwnd) { if (!wininichanges[hwnd]) - implbug("window handle %p not registered to receive WM_WININICHANGEs", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_WININICHANGEs", hwnd); wininichanges[hwnd] = false; } diff --git a/windows/grid.cpp b/windows/grid.cpp index 61e78543..cac87aff 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -562,9 +562,9 @@ static struct gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand struct gridChild *gc; if (xspan < 0) - userbug("You cannot have a negative xspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative xspan in a uiGrid cell."); if (yspan < 0) - userbug("You cannot have a negative yspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative yspan in a uiGrid cell."); gc = uiprivNew(struct gridChild); gc->c = c; gc->xspan = xspan; diff --git a/windows/menu.cpp b/windows/menu.cpp index 46405faf..09dfcf30 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -87,7 +87,7 @@ void uiMenuItemDisable(uiMenuItem *i) void uiMenuItemOnClicked(uiMenuItem *i, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (i->type == typeQuit) - userbug("You can not call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); + uiprivUserBug("You can not call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); i->onClicked = f; i->onClickedData = data; } @@ -111,7 +111,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - userbug("You can not create a new menu item after menus have been finalized."); + uiprivUserBug("You can not create a new menu item after menus have been finalized."); if (m->len >= m->cap) { m->cap += grow; @@ -169,7 +169,7 @@ uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name) uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) { if (hasQuit) - userbug("You can not have multiple Quit menu items in a program."); + uiprivUserBug("You can not have multiple Quit menu items in a program."); hasQuit = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeQuit, NULL); @@ -178,7 +178,7 @@ uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) { if (hasPreferences) - userbug("You can not have multiple Preferences menu items in a program."); + uiprivUserBug("You can not have multiple Preferences menu items in a program."); hasPreferences = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typePreferences, NULL); @@ -187,8 +187,8 @@ uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) { if (hasAbout) - // TODO place these userbug strings in a header - userbug("You can not have multiple About menu items in a program."); + // TODO place these uiprivImplBug() and uiprivUserBug() strings in a header + uiprivUserBug("You can not have multiple About menu items in a program."); hasAbout = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeAbout, NULL); @@ -204,7 +204,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - userbug("You can not create a new menu after menus have been finalized."); + uiprivUserBug("You can not create a new menu after menus have been finalized."); if (len >= cap) { cap += grow; menus = (uiMenu **) uiprivRealloc(menus, cap * sizeof (uiMenu *), "uiMenu *[]"); @@ -293,7 +293,7 @@ void runMenuEvent(WORD id, uiWindow *w) } } // no match - implbug("unknown menu ID %hu in runMenuEvent()", id); + uiprivImplBug("unknown menu ID %hu in runMenuEvent()", id); found: // first toggle checkboxes, if any @@ -316,7 +316,7 @@ static void freeMenu(uiMenu *m, HMENU submenu) if (item->hmenus[j] == submenu) break; if (j >= item->len) - implbug("submenu handle %p not found in freeMenu()", submenu); + uiprivImplBug("submenu handle %p not found in freeMenu()", submenu); for (; j < item->len - 1; j++) item->hmenus[j] = item->hmenus[j + 1]; item->hmenus[j] = NULL; @@ -352,8 +352,8 @@ void uninitMenus(void) for (j = 0; j < m->len; j++) { item = m->items[j]; if (item->len != 0) - // LONGTERM userbug()? - implbug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); + // LONGTERM uiprivUserBug()? + uiprivImplBug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); if (item->name != NULL) uiprivFree(item->name); if (item->hmenus != NULL) diff --git a/windows/progressbar.cpp b/windows/progressbar.cpp index 3750eb6a..c3a67dd3 100644 --- a/windows/progressbar.cpp +++ b/windows/progressbar.cpp @@ -54,7 +54,7 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) } if (value < 0 || value > 100) - userbug("Value %d is out of range for uiProgressBars.", value); + uiprivUserBug("Value %d is out of range for uiProgressBars.", value); if (value == 100) { // because we can't 101 SendMessageW(p->hwnd, PBM_SETRANGE32, 0, 101); From 59835a9bae97bbf8728b780ce5c2678a4e9ceb03 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 21:49:58 -0400 Subject: [PATCH 473/487] Removed the declaration of newControl(): it was completely unused, as it was superceded by uiAllocControl() long ago. --- common/OLD_uipriv.h | 3 --- common/uipriv.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index ac8ec0cc..f42baf56 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,7 +1,4 @@ -// control.c -extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); - // shouldquit.c extern int shouldQuit(void); diff --git a/common/uipriv.h b/common/uipriv.h index 530a9794..4c179c47 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -33,9 +33,6 @@ extern void uiprivDoImplBug(const char *file, const char *line, const char *func extern void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...); #define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) -// control.c -extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); - // shouldquit.c extern int shouldQuit(void); From f5be05f1437e777d50c9f8e35d5150aa6467e90b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 21:54:46 -0400 Subject: [PATCH 474/487] shouldQuit() -> uiprivShouldQuit(). --- common/OLD_uipriv.h | 3 --- common/shouldquit.c | 2 +- common/uipriv.h | 2 +- darwin/main.m | 2 +- darwin/menu.m | 2 +- unix/menu.c | 2 +- windows/menu.cpp | 2 +- windows/utilwin.cpp | 4 ++-- 8 files changed, 8 insertions(+), 11 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index f42baf56..b6e0581d 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,7 +1,4 @@ -// shouldquit.c -extern int shouldQuit(void); - // areaevents.c typedef struct clickCounter clickCounter; // you should call Reset() to zero-initialize a new instance diff --git a/common/shouldquit.c b/common/shouldquit.c index 4e7aa5c3..dddd879c 100644 --- a/common/shouldquit.c +++ b/common/shouldquit.c @@ -16,7 +16,7 @@ void uiOnShouldQuit(int (*f)(void *), void *data) onShouldQuitData = data; } -int shouldQuit(void) +int uiprivShouldQuit(void) { return (*onShouldQuit)(onShouldQuitData); } diff --git a/common/uipriv.h b/common/uipriv.h index 4c179c47..7d3ff297 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -34,7 +34,7 @@ extern void uiprivDoUserBug(const char *file, const char *line, const char *func #define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) // shouldquit.c -extern int shouldQuit(void); +extern int uiprivShouldQuit(void); // areaevents.c typedef struct clickCounter clickCounter; diff --git a/darwin/main.m b/darwin/main.m index f3392ce6..6d149fc3 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -91,7 +91,7 @@ static BOOL stepsIsRunning; { // for debugging NSLog(@"in applicationShouldTerminate:"); - if (shouldQuit()) { + if (uiprivShouldQuit()) { canQuit = YES; // this will call terminate:, which is the same as uiQuit() return NSTerminateNow; diff --git a/darwin/menu.m b/darwin/menu.m index ca6cce4b..79adbae6 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -71,7 +71,7 @@ static void mapItemReleaser(void *key, void *value) - (IBAction)onQuitClicked:(id)sender { - if (shouldQuit()) + if (uiprivShouldQuit()) uiQuit(); } diff --git a/unix/menu.c b/unix/menu.c index 3d02e939..17189c8e 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -81,7 +81,7 @@ static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data) static void onQuitClicked(uiMenuItem *item, uiWindow *w, void *data) { - if (shouldQuit()) + if (uiprivShouldQuit()) uiQuit(); } diff --git a/windows/menu.cpp b/windows/menu.cpp index 09dfcf30..65791bfb 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -68,7 +68,7 @@ static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data) static void onQuitClicked(uiMenuItem *item, uiWindow *w, void *data) { - if (shouldQuit()) + if (uiprivShouldQuit()) uiQuit(); } diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 28950674..34b72ba8 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -23,8 +23,8 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L return lResult; switch (uMsg) { case WM_QUERYENDSESSION: - // TODO block handler - if (shouldQuit()) { + // TODO block handler (TODO figure out if this meant the Vista-style block handler or not) + if (uiprivShouldQuit()) { uiQuit(); return TRUE; } From 0dddf4a490c4fad781850002807294a444756e6a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:26:51 -0400 Subject: [PATCH 475/487] clickCounter -> uiprivClickCounter. --- common/OLD_uipriv.h | 17 ----------------- common/areaevents.c | 4 ++-- common/uipriv.h | 8 ++++---- unix/area.c | 10 +++++----- windows/area.cpp | 4 ++-- windows/area.hpp | 2 +- windows/areaevents.cpp | 12 ++++++------ 7 files changed, 20 insertions(+), 37 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index b6e0581d..29583b0c 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,21 +1,4 @@ -// areaevents.c -typedef struct clickCounter clickCounter; -// you should call Reset() to zero-initialize a new instance -// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly -struct clickCounter { - int curButton; - int rectX0; - int rectY0; - int rectX1; - int rectY1; - uintptr_t prevTime; - int count; -}; -int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); -extern void clickCounterReset(clickCounter *); -extern int fromScancode(uintptr_t, uiAreaKeyEvent *); - // matrix.c extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); diff --git a/common/areaevents.c b/common/areaevents.c index 189673a2..d913b763 100644 --- a/common/areaevents.c +++ b/common/areaevents.c @@ -16,7 +16,7 @@ TODO note the bits about asymmetry and g_rcClick initial value not mattering in // x, y, xdist, ydist, and c.rect must have the same units // so must time, maxTime, and c.prevTime -int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist) +int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist) { // different button than before? if so, don't count if (button != c->curButton) @@ -50,7 +50,7 @@ int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, return c->count; } -void clickCounterReset(clickCounter *c) +void uiprivClickCounterReset(uiprivClickCounter *c) { c->curButton = 0; c->rectX0 = 0; diff --git a/common/uipriv.h b/common/uipriv.h index 7d3ff297..363450a8 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -37,10 +37,10 @@ extern void uiprivDoUserBug(const char *file, const char *line, const char *func extern int uiprivShouldQuit(void); // areaevents.c -typedef struct clickCounter clickCounter; +typedef struct uiprivClickCounter uiprivClickCounter; // you should call Reset() to zero-initialize a new instance // it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly -struct clickCounter { +struct uiprivClickCounter { int curButton; int rectX0; int rectY0; @@ -49,8 +49,8 @@ struct clickCounter { uintptr_t prevTime; int count; }; -int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); -extern void clickCounterReset(clickCounter *); +extern int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); +extern void uiprivClickCounterReset(uiprivClickCounter *); extern int fromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c diff --git a/unix/area.c b/unix/area.c index abf868ca..ca245bb0 100644 --- a/unix/area.c +++ b/unix/area.c @@ -19,7 +19,7 @@ struct areaWidget { // construct-only parameters aare not set until after the init() function has returned // we need this particular object available during init(), so put it here instead of in uiArea // keep a pointer in uiArea for convenience, though - clickCounter cc; + uiprivClickCounter cc; }; struct areaWidgetClass { @@ -45,7 +45,7 @@ struct uiArea { int scrollHeight; // note that this is a pointer; see above - clickCounter *cc; + uiprivClickCounter *cc; // for user window drags GdkEventButton *dragevent; @@ -68,7 +68,7 @@ static void areaWidget_init(areaWidget *aw) gtk_widget_set_can_focus(GTK_WIDGET(aw), TRUE); - clickCounterReset(&(aw->cc)); + uiprivClickCounterReset(&(aw->cc)); } static void areaWidget_dispose(GObject *obj) @@ -261,7 +261,7 @@ static gboolean areaWidget_button_press_event(GtkWidget *w, GdkEventButton *e) // e->time is guint32 // e->x and e->y are floating-point; just make them 32-bit integers // maxTime and maxDistance... are gint, which *should* fit, hopefully... - me.Count = clickCounterClick(a->cc, me.Down, + me.Count = uiprivClickCounterClick(a->cc, me.Down, e->x, e->y, e->time, maxTime, maxDistance, maxDistance); @@ -309,7 +309,7 @@ static gboolean onCrossing(areaWidget *aw, int left) uiArea *a = aw->a; (*(a->ah->MouseCrossed))(a->ah, a, left); - clickCounterReset(a->cc); + uiprivClickCounterReset(a->cc); return GDK_EVENT_PROPAGATE; } diff --git a/windows/area.cpp b/windows/area.cpp index ab69ff15..0042fccc 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -168,7 +168,7 @@ uiArea *uiNewArea(uiAreaHandler *ah) a->ah = ah; a->scrolling = FALSE; - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); // a->hwnd is assigned in areaWndProc() uiWindowsEnsureCreateControlHWND(0, @@ -190,7 +190,7 @@ uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height) a->scrolling = TRUE; a->scrollWidth = width; a->scrollHeight = height; - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); // a->hwnd is assigned in areaWndProc() uiWindowsEnsureCreateControlHWND(0, diff --git a/windows/area.hpp b/windows/area.hpp index 86a62de6..dfc2bc58 100644 --- a/windows/area.hpp +++ b/windows/area.hpp @@ -18,7 +18,7 @@ struct uiArea { int hwheelCarry; int vwheelCarry; - clickCounter cc; + uiprivClickCounter cc; BOOL capturing; BOOL inside; diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 615c06ea..4bf55665 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -92,11 +92,11 @@ static void areaMouseEvent(uiArea *a, int down, int up, WPARAM wParam, LPARAM l if (inClient && !a->inside) { a->inside = TRUE; (*(a->ah->MouseCrossed))(a->ah, a, 0); - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } else if (!inClient && a->inside) { a->inside = FALSE; (*(a->ah->MouseCrossed))(a->ah, a, 1); - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } } @@ -120,7 +120,7 @@ static void areaMouseEvent(uiArea *a, int down, int up, WPARAM wParam, LPARAM l // GetMessageTime() returns LONG and GetDoubleClckTime() returns UINT, which are int32 and uint32, respectively, but we don't need to worry about the signedness because for the same bit widths and two's complement arithmetic, s1-s2 == u1-u2 if bits(s1)==bits(s2) and bits(u1)==bits(u2) (and Windows requires two's complement: http://blogs.msdn.com/b/oldnewthing/archive/2005/05/27/422551.aspx) // signedness isn't much of an issue for these calls anyway because http://stackoverflow.com/questions/24022225/what-are-the-sign-extension-rules-for-calling-windows-api-functions-stdcall-t and that we're only using unsigned values (think back to how you (didn't) handle signedness in assembly language) AND because of the above AND because the statistics below (time interval and width/height) really don't make sense if negative // GetSystemMetrics() returns int, which is int32 - me.Count = clickCounterClick(&(a->cc), me.Down, + me.Count = uiprivClickCounterClick(&(a->cc), me.Down, me.X, me.Y, GetMessageTime(), GetDoubleClickTime(), GetSystemMetrics(SM_CXDOUBLECLK) / 2, @@ -164,7 +164,7 @@ static void onMouseEntered(uiArea *a) track(a, TRUE); (*(a->ah->MouseCrossed))(a->ah, a, 0); // TODO figure out why we did this to begin with; either we do it on both GTK+ and Windows or not at all - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } // TODO genericize it so that it can be called above @@ -174,7 +174,7 @@ static void onMouseLeft(uiArea *a) a->inside = FALSE; (*(a->ah->MouseCrossed))(a->ah, a, 1); // TODO figure out why we did this to begin with; either we do it on both GTK+ and Windows or not at all - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } // we use VK_SNAPSHOT as a sentinel because libui will never support the print screen key; that key belongs to the user @@ -325,7 +325,7 @@ BOOL areaDoEvents(uiArea *a, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *l switch (uMsg) { case WM_ACTIVATE: // don't keep the double-click timer running if the user switched programs in between clicks - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); *lResult = 0; return TRUE; case WM_MOUSEMOVE: From 24a4b0997c65ea052760657058b338c79046c5e2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:31:17 -0400 Subject: [PATCH 476/487] fromScancode() -> uiprivFromScancode(). --- common/areaevents.c | 2 +- common/uipriv.h | 2 +- unix/area.c | 2 +- windows/areaevents.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/areaevents.c b/common/areaevents.c index d913b763..491a7283 100644 --- a/common/areaevents.c +++ b/common/areaevents.c @@ -151,7 +151,7 @@ static const struct { { 0xFFFF, 0 }, }; -int fromScancode(uintptr_t scancode, uiAreaKeyEvent *ke) +int uiprivFromScancode(uintptr_t scancode, uiAreaKeyEvent *ke) { int i; diff --git a/common/uipriv.h b/common/uipriv.h index 363450a8..8fc3d42e 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -51,7 +51,7 @@ struct uiprivClickCounter { }; extern int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); extern void uiprivClickCounterReset(uiprivClickCounter *); -extern int fromScancode(uintptr_t, uiAreaKeyEvent *); +extern int uiprivFromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); diff --git a/unix/area.c b/unix/area.c index ca245bb0..cba1f5e7 100644 --- a/unix/area.c +++ b/unix/area.c @@ -411,7 +411,7 @@ static int areaKeyEvent(uiArea *a, int up, GdkEventKey *e) goto keyFound; } - if (fromScancode(e->hardware_keycode - 8, &ke)) + if (uiprivFromScancode(e->hardware_keycode - 8, &ke)) goto keyFound; // no supported key found; treat as unhandled diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 4bf55665..c7014ecb 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -300,7 +300,7 @@ static int areaKeyEvent(uiArea *a, int up, WPARAM wParam, LPARAM lParam) } // and finally everything else - if (fromScancode((lParam >> 16) & 0xFF, &ke)) + if (uiprivFromScancode((lParam >> 16) & 0xFF, &ke)) goto keyFound; // not a supported key, assume unhandled From 01d6422664d56bbf3c9078ec3a7b2fbda39bd2ef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:39:34 -0400 Subject: [PATCH 477/487] Added uipriv prefixes to the matrix functions. --- common/OLD_uipriv.h | 5 ----- common/matrix.c | 6 +++--- common/uipriv.h | 6 +++--- darwin/draw.m | 4 ++-- unix/drawmatrix.c | 4 ++-- windows/drawmatrix.cpp | 2 +- 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 29583b0c..95ab829c 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,8 +1,3 @@ -// matrix.c -extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); -extern void scaleCenter(double, double, double *, double *); -extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); - // OS-specific text.* files extern int uiprivStricmp(const char *a, const char *b); diff --git a/common/matrix.c b/common/matrix.c index 676885d1..93d4d357 100644 --- a/common/matrix.c +++ b/common/matrix.c @@ -18,7 +18,7 @@ void uiDrawMatrixSetIdentity(uiDrawMatrix *m) // see https://msdn.microsoft.com/en-us/library/windows/desktop/ff684171%28v=vs.85%29.aspx#skew_transform // TODO see if there's a way we can avoid the multiplication -void fallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) +void uiprivFallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) { uiDrawMatrix n; @@ -31,7 +31,7 @@ void fallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double ya uiDrawMatrixMultiply(m, &n); } -void scaleCenter(double xCenter, double yCenter, double *x, double *y) +void uiprivScaleCenter(double xCenter, double yCenter, double *x, double *y) { *x = xCenter - (*x * xCenter); *y = yCenter - (*y * yCenter); @@ -39,7 +39,7 @@ void scaleCenter(double xCenter, double yCenter, double *x, double *y) // the basic algorithm is from cairo // but it's the same algorithm as the transform point, just without M31 and M32 taken into account, so let's just do that instead -void fallbackTransformSize(uiDrawMatrix *m, double *x, double *y) +void uiprivFallbackTransformSize(uiDrawMatrix *m, double *x, double *y) { uiDrawMatrix m2; diff --git a/common/uipriv.h b/common/uipriv.h index 8fc3d42e..f9ad4c41 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -54,9 +54,9 @@ extern void uiprivClickCounterReset(uiprivClickCounter *); extern int uiprivFromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c -extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); -extern void scaleCenter(double, double, double *, double *); -extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); +extern void uiprivFallbackSkew(uiDrawMatrix *, double, double, double, double); +extern void uiprivScaleCenter(double, double, double *, double *); +extern void uiprivFallbackTransformSize(uiDrawMatrix *, double *, double *); // OS-specific text.* files extern int uiprivStricmp(const char *a, const char *b); diff --git a/darwin/draw.m b/darwin/draw.m index 7ca854d2..cf7d8f13 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -334,7 +334,7 @@ void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x m2c(m, &c); xt = x; yt = y; - scaleCenter(xCenter, yCenter, &xt, &yt); + uiprivScaleCenter(xCenter, yCenter, &xt, &yt); c = CGAffineTransformTranslate(c, xt, yt); c = CGAffineTransformScale(c, x, y); c = CGAffineTransformTranslate(c, -xt, -yt); @@ -354,7 +354,7 @@ void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) { - fallbackSkew(m, x, y, xamount, yamount); + uiprivFallbackSkew(m, x, y, xamount, yamount); } void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src) diff --git a/unix/drawmatrix.c b/unix/drawmatrix.c index ac7ac579..7d15d920 100644 --- a/unix/drawmatrix.c +++ b/unix/drawmatrix.c @@ -39,7 +39,7 @@ void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x m2c(m, &c); xt = x; yt = y; - scaleCenter(xCenter, yCenter, &xt, &yt); + uiprivScaleCenter(xCenter, yCenter, &xt, &yt); cairo_matrix_translate(&c, xt, yt); cairo_matrix_scale(&c, x, y); cairo_matrix_translate(&c, -xt, -yt); @@ -59,7 +59,7 @@ void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) { - fallbackSkew(m, x, y, xamount, yamount); + uiprivFallbackSkew(m, x, y, xamount, yamount); } void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src) diff --git a/windows/drawmatrix.cpp b/windows/drawmatrix.cpp index 090972a5..4ddc5e9a 100644 --- a/windows/drawmatrix.cpp +++ b/windows/drawmatrix.cpp @@ -113,5 +113,5 @@ void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y) void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y) { - fallbackTransformSize(m, x, y); + uiprivFallbackTransformSize(m, x, y); } From 8e2004cf6773cc8d880f269d4e0451af229e0729 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:43:14 -0400 Subject: [PATCH 478/487] struct graphemes -> uiprivGraphemes. Also deleted OLD_uipriv.h now that that one is settled. --- common/OLD_uipriv.h | 3 --- common/attrstr.c | 2 +- common/attrstr.h | 5 +++-- darwin/graphemes.m | 6 +++--- unix/graphemes.c | 6 +++--- windows/graphemes.cpp | 6 +++--- 6 files changed, 13 insertions(+), 15 deletions(-) delete mode 100644 common/OLD_uipriv.h diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h deleted file mode 100644 index 95ab829c..00000000 --- a/common/OLD_uipriv.h +++ /dev/null @@ -1,3 +0,0 @@ - -// OS-specific text.* files -extern int uiprivStricmp(const char *a, const char *b); diff --git a/common/attrstr.c b/common/attrstr.c index ee2b1616..2445c330 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -18,7 +18,7 @@ struct uiAttributedString { size_t *u16tou8; // this is lazily created to keep things from getting *too* slow - struct graphemes *graphemes; + uiprivGraphemes *graphemes; }; static void resize(uiAttributedString *s, size_t u8, size_t u16) diff --git a/common/attrstr.h b/common/attrstr.h index 54e43feb..475589f3 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -28,10 +28,11 @@ extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedStri extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n); // per-OS graphemes.c/graphemes.cpp/graphemes.m/etc. -struct graphemes { +typedef struct uiprivGraphemes uiprivGraphemes; +struct uiprivGraphemes { size_t len; size_t *pointsToGraphemes; size_t *graphemesToPoints; }; extern int uiprivGraphemesTakesUTF16(void); -extern struct graphemes *uiprivNewGraphemes(void *s, size_t len); +extern uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len); diff --git a/darwin/graphemes.m b/darwin/graphemes.m index 3fdbc17e..a92534f3 100644 --- a/darwin/graphemes.m +++ b/darwin/graphemes.m @@ -10,16 +10,16 @@ int uiprivGraphemesTakesUTF16(void) return 1; } -struct graphemes *uiprivNewGraphemes(void *s, size_t len) +uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len) { - struct graphemes *g; + uiprivGraphemes *g; UniChar *str = (UniChar *) s; CFStringRef cfstr; size_t ppos, gpos; CFRange range; size_t i; - g = uiprivNew(struct graphemes); + g = uiprivNew(uiprivGraphemes); cfstr = CFStringCreateWithCharactersNoCopy(NULL, str, len, kCFAllocatorNull); if (cfstr == NULL) { diff --git a/unix/graphemes.c b/unix/graphemes.c index b5edb95a..952f1ef8 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -7,16 +7,16 @@ int uiprivGraphemesTakesUTF16(void) return 0; } -struct graphemes *uiprivNewGraphemes(void *s, size_t len) +uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len) { - struct graphemes *g; + uiprivGraphemes *g; char *text = (char *) s; size_t lenchars; PangoLogAttr *logattrs; size_t i; size_t *op; - g = uiprivNew(struct graphemes); + g = uiprivNew(uiprivGraphemes); // TODO see if we can use the utf routines lenchars = g_utf8_strlen(text, -1); diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 266cf97f..c11dd203 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -11,13 +11,13 @@ int uiprivGraphemesTakesUTF16(void) return 1; } -struct graphemes *uiprivNewGraphemes(void *s, size_t len) +uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len) { - struct graphemes *g; + uiprivGraphemes *g; WCHAR *str; size_t *pPTG, *pGTP; - g = uiprivNew(struct graphemes); + g = uiprivNew(uiprivGraphemes); g->len = 0; str = (WCHAR *) s; From c3992cc6473c640c2d260d4e7e18bd95464614f1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 23:08:57 -0400 Subject: [PATCH 479/487] uipriv-ized utf.c. --- common/attrstr.c | 12 ++++++------ common/controlsigs.h | 2 ++ common/utf.c | 41 +++++++++++++++++++++-------------------- common/utf.h | 36 ++++++++++++++++++++---------------- windows/utf16.cpp | 12 ++++++------ 5 files changed, 55 insertions(+), 48 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 2445c330..f2cdb66b 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -93,11 +93,11 @@ static void u8u16len(const char *str, size_t *n8, size_t *n16) *n8 = 0; *n16 = 0; while (*str) { - str = utf8DecodeRune(str, 0, &rune); + str = uiprivUTF8DecodeRune(str, 0, &rune); // TODO document the use of the function vs a pointer subtract here // TODO also we need to consider namespace collision with utf.h... - *n8 += utf8EncodeRune(rune, buf); - *n16 += utf16EncodeRune(rune, buf16); + *n8 += uiprivUTF8EncodeRune(rune, buf); + *n16 += uiprivUTF16EncodeRune(rune, buf16); } } @@ -179,9 +179,9 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s while (*str) { size_t n; - str = utf8DecodeRune(str, 0, &rune); - n = utf8EncodeRune(rune, buf); - n16 = utf16EncodeRune(rune, buf16); + str = uiprivUTF8DecodeRune(str, 0, &rune); + n = uiprivUTF8EncodeRune(rune, buf); + n16 = uiprivUTF16EncodeRune(rune, buf16); s->s[old] = buf[0]; s->u8tou16[old] = old16; if (n > 1) { diff --git a/common/controlsigs.h b/common/controlsigs.h index 1cbf18d5..06397187 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -1,5 +1,7 @@ // 24 april 2016 +// TODO if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so... + #define uiAreaSignature 0x41726561 #define uiBoxSignature 0x426F784C #define uiButtonSignature 0x42746F6E diff --git a/common/utf.c b/common/utf.c index 9efb9493..5577529b 100644 --- a/common/utf.c +++ b/common/utf.c @@ -1,5 +1,6 @@ // utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ // 10 november 2016 +// function names have been altered to avoid namespace collisions in libui static builds (see utf.h) #include "utf.h" // this code imitates Go's unicode/utf8 and unicode/utf16 @@ -9,7 +10,7 @@ // encoded must be at most 4 bytes // TODO clean this code up somehow -size_t utf8EncodeRune(uint32_t rune, char *encoded) +size_t uiprivUTF8EncodeRune(uint32_t rune, char *encoded) { uint8_t b, c, d, e; size_t n; @@ -72,7 +73,7 @@ done: return n; } -const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune) +const char *uiprivUTF8DecodeRune(const char *s, size_t nElem, uint32_t *rune) { uint8_t b, c; uint8_t lowestAllowed, highestAllowed; @@ -172,7 +173,7 @@ const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune) } // encoded must have at most 2 elements -size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded) +size_t uiprivUTF16EncodeRune(uint32_t rune, uint16_t *encoded) { uint16_t low, high; @@ -198,7 +199,7 @@ size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded) } // TODO see if this can be cleaned up somehow -const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) +const uint16_t *uiprivUTF16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) { uint16_t high, low; @@ -240,7 +241,7 @@ const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) // TODO find a way to reduce the code in all of these somehow // TODO find a way to remove u as well -size_t utf8RuneCount(const char *s, size_t nElem) +size_t uiprivUTF8RuneCount(const char *s, size_t nElem) { size_t len; uint32_t rune; @@ -251,7 +252,7 @@ size_t utf8RuneCount(const char *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf8DecodeRune(t, nElem, &rune); + u = uiprivUTF8DecodeRune(t, nElem, &rune); len++; nElem -= u - t; t = u; @@ -260,13 +261,13 @@ size_t utf8RuneCount(const char *s, size_t nElem) } len = 0; while (*s) { - s = utf8DecodeRune(s, nElem, &rune); + s = uiprivUTF8DecodeRune(s, nElem, &rune); len++; } return len; } -size_t utf8UTF16Count(const char *s, size_t nElem) +size_t uiprivUTF8UTF16Count(const char *s, size_t nElem) { size_t len; uint32_t rune; @@ -278,8 +279,8 @@ size_t utf8UTF16Count(const char *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf8DecodeRune(t, nElem, &rune); - len += utf16EncodeRune(rune, encoded); + u = uiprivUTF8DecodeRune(t, nElem, &rune); + len += uiprivUTF16EncodeRune(rune, encoded); nElem -= u - t; t = u; } @@ -287,13 +288,13 @@ size_t utf8UTF16Count(const char *s, size_t nElem) } len = 0; while (*s) { - s = utf8DecodeRune(s, nElem, &rune); - len += utf16EncodeRune(rune, encoded); + s = uiprivUTF8DecodeRune(s, nElem, &rune); + len += uiprivUTF16EncodeRune(rune, encoded); } return len; } -size_t utf16RuneCount(const uint16_t *s, size_t nElem) +size_t uiprivUTF16RuneCount(const uint16_t *s, size_t nElem) { size_t len; uint32_t rune; @@ -304,7 +305,7 @@ size_t utf16RuneCount(const uint16_t *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf16DecodeRune(t, nElem, &rune); + u = uiprivUTF16DecodeRune(t, nElem, &rune); len++; nElem -= u - t; t = u; @@ -313,13 +314,13 @@ size_t utf16RuneCount(const uint16_t *s, size_t nElem) } len = 0; while (*s) { - s = utf16DecodeRune(s, nElem, &rune); + s = uiprivUTF16DecodeRune(s, nElem, &rune); len++; } return len; } -size_t utf16UTF8Count(const uint16_t *s, size_t nElem) +size_t uiprivUTF16UTF8Count(const uint16_t *s, size_t nElem) { size_t len; uint32_t rune; @@ -331,8 +332,8 @@ size_t utf16UTF8Count(const uint16_t *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf16DecodeRune(t, nElem, &rune); - len += utf8EncodeRune(rune, encoded); + u = uiprivUTF16DecodeRune(t, nElem, &rune); + len += uiprivUTF8EncodeRune(rune, encoded); nElem -= u - t; t = u; } @@ -340,8 +341,8 @@ size_t utf16UTF8Count(const uint16_t *s, size_t nElem) } len = 0; while (*s) { - s = utf16DecodeRune(s, nElem, &rune); - len += utf8EncodeRune(rune, encoded); + s = uiprivUTF16DecodeRune(s, nElem, &rune); + len += uiprivUTF8EncodeRune(rune, encoded); } return len; } diff --git a/common/utf.h b/common/utf.h index b810a49d..41e556f8 100644 --- a/common/utf.h +++ b/common/utf.h @@ -1,25 +1,29 @@ // utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ // 10 november 2016 +// note the overridden names with uipriv at the beginning; this avoids potential symbol clashes when building libui as a static library +// LONGTERM find a way to encode the name overrides directly into the utf library + #ifdef __cplusplus extern "C" { #endif +// TODO (for utf itself as well) should this go outside the extern "C" block or not #include #include // if nElem == 0, assume the buffer has no upper limit and is '\0' terminated // otherwise, assume buffer is NOT '\0' terminated but is bounded by nElem *elements* -extern size_t utf8EncodeRune(uint32_t rune, char *encoded); -extern const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune); -extern size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded); -extern const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune); +extern size_t uiprivUTF8EncodeRune(uint32_t rune, char *encoded); +extern const char *uiprivUTF8DecodeRune(const char *s, size_t nElem, uint32_t *rune); +extern size_t uiprivUTF16EncodeRune(uint32_t rune, uint16_t *encoded); +extern const uint16_t *uiprivUTF16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune); -extern size_t utf8RuneCount(const char *s, size_t nElem); -extern size_t utf8UTF16Count(const char *s, size_t nElem); -extern size_t utf16RuneCount(const uint16_t *s, size_t nElem); -extern size_t utf16UTF8Count(const uint16_t *s, size_t nElem); +extern size_t uiprivUTF8RuneCount(const char *s, size_t nElem); +extern size_t uiprivUTF8UTF16Count(const char *s, size_t nElem); +extern size_t uiprivUTF16RuneCount(const uint16_t *s, size_t nElem); +extern size_t uiprivUTF16UTF8Count(const uint16_t *s, size_t nElem); #ifdef __cplusplus } @@ -33,27 +37,27 @@ extern size_t utf16UTF8Count(const uint16_t *s, size_t nElem); // TODO same for UniChar/unichar on Mac? if both are unsigned then we have nothing to worry about #if defined(_MSC_VER) -inline size_t utf16EncodeRune(uint32_t rune, __wchar_t *encoded) +inline size_t uiprivUTF16EncodeRune(uint32_t rune, __wchar_t *encoded) { - return utf16EncodeRune(rune, reinterpret_cast(encoded)); + return uiprivUTF16EncodeRune(rune, reinterpret_cast(encoded)); } -inline const __wchar_t *utf16DecodeRune(const __wchar_t *s, size_t nElem, uint32_t *rune) +inline const __wchar_t *uiprivUTF16DecodeRune(const __wchar_t *s, size_t nElem, uint32_t *rune) { const uint16_t *ret; - ret = utf16DecodeRune(reinterpret_cast(s), nElem, rune); + ret = uiprivUTF16DecodeRune(reinterpret_cast(s), nElem, rune); return reinterpret_cast(ret); } -inline size_t utf16RuneCount(const __wchar_t *s, size_t nElem) +inline size_t uiprivUTF16RuneCount(const __wchar_t *s, size_t nElem) { - return utf16RuneCount(reinterpret_cast(s), nElem); + return uiprivUTF16RuneCount(reinterpret_cast(s), nElem); } -inline size_t utf16UTF8Count(const __wchar_t *s, size_t nElem) +inline size_t uiprivUTF16UTF8Count(const __wchar_t *s, size_t nElem) { - return utf16UTF8Count(reinterpret_cast(s), nElem); + return uiprivUTF16UTF8Count(reinterpret_cast(s), nElem); } #endif diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 6afd0b0e..b9e57599 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -12,12 +12,12 @@ WCHAR *toUTF16(const char *str) if (*str == '\0') // empty string return emptyUTF16(); - n = utf8UTF16Count(str, 0); + n = uiprivUTF8UTF16Count(str, 0); wstr = (WCHAR *) uiprivAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); wp = wstr; while (*str) { - str = utf8DecodeRune(str, 0, &rune); - n = utf16EncodeRune(rune, wp); + str = uiprivUTF8DecodeRune(str, 0, &rune); + n = uiprivUTF16EncodeRune(rune, wp); wp += n; } return wstr; @@ -32,12 +32,12 @@ char *toUTF8(const WCHAR *wstr) if (*wstr == L'\0') // empty string return emptyUTF8(); - n = utf16RuneCount(wstr, 0); + n = uiprivUTF16RuneCount(wstr, 0); str = (char *) uiprivAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { - wstr = utf16DecodeRune(wstr, 0, &rune); - n = utf8EncodeRune(rune, sp); + wstr = uiprivUTF16DecodeRune(wstr, 0, &rune); + n = uiprivUTF8EncodeRune(rune, sp); sp += n; } return str; From fcc26ab9a97ce14dc1bc3334e8901683b1aacc85 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 23:10:11 -0400 Subject: [PATCH 480/487] =?UTF-8?q?Fixed=20a=20serious=20bug=20in=20window?= =?UTF-8?q?s/utf16.cpp=20that=20went=20unnoticed=20for=20this=20long:=20we?= =?UTF-8?q?=20wanted=20utf16UTF8Count(),=20not=20utf16RuneCount(),=20in=20?= =?UTF-8?q?toUTF8();=20any=20non-ASCII=20text=20had=20the=20wrong=20number?= =?UTF-8?q?=20of=20bytes,=20and=20thus=20random=20heap=20corruption.=20The?= =?UTF-8?q?=20string=20"=E9=8E=BF=E5=B6=84=E7=B6=94=E9=8E=B4=E6=84=AC?= =?UTF-8?q?=E5=A7=9B=E7=80=B9=E5=B1=BE=E5=9E=9A=E9=8A=86"=20(taken=20from?= =?UTF-8?q?=20the=20completely=20unrelated=20#337)=20and=20the=20Set=20But?= =?UTF-8?q?ton=20Text=20button=20was=20enough=20to=20trigger=20this.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- windows/utf16.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/utf16.cpp b/windows/utf16.cpp index b9e57599..131759e9 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -32,7 +32,7 @@ char *toUTF8(const WCHAR *wstr) if (*wstr == L'\0') // empty string return emptyUTF8(); - n = uiprivUTF16RuneCount(wstr, 0); + n = uiprivUTF16UTF8Count(wstr, 0); str = (char *) uiprivAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { From b3df05eb8eefd2270f6fcbd62b709f9a237cfda8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 23:16:04 -0400 Subject: [PATCH 481/487] More TODOs. (This was originally added in a prior commit but I forgot to mention it; I wanted it to be LONGTERM but forgot about that the first time.) --- common/controlsigs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/controlsigs.h b/common/controlsigs.h index 06397187..944afa9b 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -1,6 +1,6 @@ // 24 april 2016 -// TODO if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so... +// LONGTERM if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so... #define uiAreaSignature 0x41726561 #define uiBoxSignature 0x426F784C From 7cd88ddd03cd912aeca3f6d6f8acf2d15b0bcc5a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 Apr 2018 01:33:21 -0400 Subject: [PATCH 482/487] More notes. --- _notes/misc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_notes/misc b/_notes/misc index 5d40d908..2fd78a92 100644 --- a/_notes/misc +++ b/_notes/misc @@ -184,3 +184,9 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/aa969513(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/aa969527(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/dd388198(v=vs.85).aspx I hope the MFC ribbon can have customizable colors too, otherwise I'll have to do some serious image processing... + +windows debugging +https://msdn.microsoft.com/en-us/library/windows/desktop/hh780351(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ff476881(v=vs.85).aspx#Debug + https://msdn.microsoft.com/en-us/library/windows/desktop/hh780343(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/dn457937(v=vs.85).aspx From 750f4214b72c42924c9fd5dfbd4d40d3c9352193 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 Apr 2018 02:31:24 -0400 Subject: [PATCH 483/487] Cleaned up private symbols (and in one case, explicit initialization) of symbols in all *.c files in common/. Now to decide what to do about whether uipriv.h should include ui.h and if attrstr.h should even stay, and then I can merge this back. --- common/control.c | 4 ++-- common/shouldquit.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/control.c b/common/control.c index 3b5b8286..98cb94aa 100644 --- a/common/control.c +++ b/common/control.c @@ -57,14 +57,14 @@ void uiControlDisable(uiControl *c) (*(c->Disable))(c); } -#define uiControlSignature 0x7569436F +#define uiprivControlSignature 0x7569436F uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr) { uiControl *c; c = (uiControl *) uiprivAlloc(size, typenamestr); - c->Signature = uiControlSignature; + c->Signature = uiprivControlSignature; c->OSSignature = OSsig; c->TypeSignature = typesig; return c; diff --git a/common/shouldquit.c b/common/shouldquit.c index dddd879c..df57b6c5 100644 --- a/common/shouldquit.c +++ b/common/shouldquit.c @@ -8,7 +8,7 @@ static int defaultOnShouldQuit(void *data) } static int (*onShouldQuit)(void *) = defaultOnShouldQuit; -static void *onShouldQuitData; +static void *onShouldQuitData = NULL; void uiOnShouldQuit(int (*f)(void *), void *data) { From ee986363c86ceaaa73eaf083735c050a3d8458b7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 Apr 2018 20:35:47 -0400 Subject: [PATCH 484/487] More notes. --- _notes/misc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/_notes/misc b/_notes/misc index 2fd78a92..649d6cb4 100644 --- a/_notes/misc +++ b/_notes/misc @@ -190,3 +190,8 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/hh780351(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ff476881(v=vs.85).aspx#Debug https://msdn.microsoft.com/en-us/library/windows/desktop/hh780343(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/dn457937(v=vs.85).aspx + +more OS2 stuff +https://www.google.com/search?q=%22ibm+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1 +https://www.google.com/search?q=%22os%2F2+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1 +http://www.edm2.com/index.php/IBM_OS/2_Developer%27s_Packages From 4df47a6ee8a05f1109b60f6bd45f65c957ac3deb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Apr 2018 21:04:04 -0400 Subject: [PATCH 485/487] Decided what to do about uipriv.h including ui.h (uipriv_OS.h* wrecks that plan, so make a note of us not doing it). Now to just decide what to do about attrstr.h and then we can merge back. --- common/uipriv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/uipriv.h b/common/uipriv.h index f9ad4c41..6441ada5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,4 +1,5 @@ // 6 april 2015 +// note: this file should not include ui.h, as the OS-specific ui_*.h files are included between that one and this one in the OS-specific uipriv_*.h* files #include #include #include "controlsigs.h" From 52dc39a5537432c34cd1cdd17faf10fff77bb86f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 00:54:24 -0400 Subject: [PATCH 486/487] Decided to keep attrstr.h for now; removed extern "C" hack from attrstr.hpp on Windows and moved it into attrstr.h. Let's merge back. --- common/attrstr.h | 8 ++++++++ windows/attrstr.hpp | 2 -- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index 475589f3..69ada5c1 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,5 +1,9 @@ // 19 february 2018 +#ifdef __cplusplus +extern "C" { +#endif + // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); extern void uiprivAttributeRelease(uiAttribute *a); @@ -36,3 +40,7 @@ struct uiprivGraphemes { }; extern int uiprivGraphemesTakesUTF16(void); extern uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 449000ed..bd522ca1 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -1,7 +1,5 @@ // 11 march 2018 -extern "C" { #include "../common/attrstr.h" -} // dwrite.cpp extern IDWriteFactory *dwfactory; From 9cf6c3faf560d0ab69a46a540780ae405323dde8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 00:57:53 -0400 Subject: [PATCH 487/487] Updated the README with the previous merge. Update #308. Oops, forgot to do this with the merge... --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cfcd02d6..355bb009 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,9 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* +* **18 April 2018** + * Migrated all code in the `common/` directory to use `uipriv` prefixes for everything that isn't `static`. This is the first step toward fixing static library oddities within libui, allowing libui to truly be safely used as either a static library or a shared library. + * **17 June 2016** * `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop. * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.