From f8fc796c959a20af95aa29b82dceab6ba274f44a Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Wed, 3 Nov 2021 15:34:42 +0000 Subject: [PATCH] Bug797501 - Currency symbols in Hebrew (RTL) language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Gnucash is run in Hebrew which is a RTL language, on the accounts page the tree view is displaying the required number as the following... TreeView entry is '1,500.00 ₪' or '-1,500.00 ₪' TreeModel string is '₪ 1,500.00‬' or '₪ 1,500.00-‬' This seems to be down to the GTK 'Unicode Bidirectional Text Algorithm' which is changing the representation of the model string based on the first strongly typed character, in this case the Israeli shekel sign. To fix this, when creating the displayed monetary amount insert a BiDi ltr isolate uni-character at the start of the string. --- gnucash/gnome-utils/gnc-tree-model-account.c | 5 +- libgnucash/app-utils/gnc-ui-balances.c | 12 +++-- libgnucash/app-utils/gnc-ui-util.c | 53 +++++++++++++++++++- libgnucash/app-utils/gnc-ui-util.h | 26 +++++++++- 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/gnucash/gnome-utils/gnc-tree-model-account.c b/gnucash/gnome-utils/gnc-tree-model-account.c index 5c3160e5acc..e04eac9adc0 100644 --- a/gnucash/gnome-utils/gnc-tree-model-account.c +++ b/gnucash/gnome-utils/gnc-tree-model-account.c @@ -560,6 +560,7 @@ gnc_tree_model_account_compute_period_balance (GncTreeModelAccount *model, gboolean *negative) { GncTreeModelAccountPrivate *priv; + GNCPrintAmountInfo print_info; time64 t1, t2; gnc_numeric b3; @@ -583,7 +584,9 @@ gnc_tree_model_account_compute_period_balance (GncTreeModelAccount *model, if (negative) *negative = gnc_numeric_negative_p (b3); - return g_strdup(xaccPrintAmount (b3, gnc_account_print_info (acct, TRUE))); + print_info = gnc_account_print_info (acct, TRUE); + + return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (b3, print_info)); } static gboolean diff --git a/libgnucash/app-utils/gnc-ui-balances.c b/libgnucash/app-utils/gnc-ui-balances.c index b46b531996c..9227aefb2c4 100644 --- a/libgnucash/app-utils/gnc-ui-balances.c +++ b/libgnucash/app-utils/gnc-ui-balances.c @@ -146,7 +146,8 @@ gnc_ui_account_get_print_balance (xaccGetBalanceInCurrencyFn fn, balance = gnc_ui_account_get_balance_full(fn, account, recurse, negative, NULL); print_info = gnc_account_print_info(account, TRUE); - return g_strdup(xaccPrintAmount(balance, print_info)); + + return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); } @@ -178,7 +179,8 @@ gnc_ui_account_get_print_report_balance (xaccGetBalanceInCurrencyFn fn, balance = gnc_ui_account_get_balance_full(fn, account, recurse, negative, report_commodity); print_info = gnc_commodity_print_info(report_commodity, TRUE); - return g_strdup(xaccPrintAmount(balance, print_info)); + + return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); } static gnc_numeric @@ -312,7 +314,8 @@ gnc_ui_owner_get_print_balance (GncOwner *owner, balance = gnc_ui_owner_get_balance_full (owner, negative, NULL); print_info = gnc_commodity_print_info (gncOwnerGetCurrency (owner), TRUE); - return g_strdup (xaccPrintAmount (balance, print_info)); + + return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); } /** @@ -338,6 +341,7 @@ gnc_ui_owner_get_print_report_balance (GncOwner *owner, balance = gnc_ui_owner_get_balance_full (owner, negative, report_commodity); print_info = gnc_commodity_print_info (report_commodity, TRUE); - return g_strdup (xaccPrintAmount (balance, print_info)); + + return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); } diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c index 5bcb8eb638c..d391bb3a9e0 100644 --- a/libgnucash/app-utils/gnc-ui-util.c +++ b/libgnucash/app-utils/gnc-ui-util.c @@ -1850,11 +1850,13 @@ xaccSPrintAmount (char * bufp, gnc_numeric val, GNCPrintAmountInfo info) return (bufp - orig_bufp); } +#define BUFLEN 1024 + const char * xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info) { /* hack alert -- this is not thread safe ... */ - static char buf[1024]; + static char buf[BUFLEN]; if (!xaccSPrintAmount (buf, val, info)) buf[0] = '\0'; @@ -1863,6 +1865,55 @@ xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info) return buf; } +const char * +gnc_print_amount_with_bidi_ltr_isolate (gnc_numeric val, GNCPrintAmountInfo info) +{ + /* hack alert -- this is not thread safe ... */ + static char buf[BUFLEN]; + static const char ltr_isolate[] = { 0xe2, 0x81, 0xa6 }; + static const char ltr_pop_isolate[] = { 0xe2, 0x81, 0xa9 }; + size_t offset = info.use_symbol ? 3 : 0; + + memset (buf, 0, BUFLEN); + if (!xaccSPrintAmount (buf + offset, val, info)) + { + buf[0] = '\0'; + return buf; + }; + + if (!info.use_symbol) + return buf; + + memcpy (buf, ltr_isolate, 3); + + if (buf[BUFLEN - 4] == '\0') + { + size_t length = strlen (buf); + memcpy (buf + length, ltr_pop_isolate, 3); + } + else + { + buf[BUFLEN - 1] = '\0'; + memcpy (buf + BUFLEN - 4, ltr_pop_isolate, 3); + + PWARN("buffer length %d exceeded, string truncated was %s", BUFLEN, buf); + } + /* its OK to return buf, since we declared it static + and is immediately g_strdup'd */ + return buf; +} + +gchar * +gnc_wrap_text_with_bidi_ltr_isolate (const gchar *text) +{ + static const char *ltr = "\u2066"; // ltr isolate + static const char *pop = "\u2069"; // pop directional formatting + + if (!text) + return NULL; + + return g_strconcat (ltr, text, pop, NULL); +} /********************************************************************\ ********************************************************************/ diff --git a/libgnucash/app-utils/gnc-ui-util.h b/libgnucash/app-utils/gnc-ui-util.h index b9bb385c60b..4e8867d1039 100644 --- a/libgnucash/app-utils/gnc-ui-util.h +++ b/libgnucash/app-utils/gnc-ui-util.h @@ -401,6 +401,28 @@ xaccParseAmountExtended (const char * in_str, gboolean monetary, gunichar group_separator, const char *ignore_list, gnc_numeric *result, char **endstr); +/** + * Make a string representation of a gnc_numeric. Warning, the + * gnc_numeric is not checked for validity and the returned char* may + * point to random garbage. + * + * This is the same as xaccPrintAmount but wraps the output with BiDi + * left to right isolate if a symbol is displayed. + */ +const char * +gnc_print_amount_with_bidi_ltr_isolate (gnc_numeric val, GNCPrintAmountInfo info); + +/** + * This function helps with GTK's use of 'Unicode Bidirectional + * Text Algorithm'. To keep the format of the text, this function wraps + * the text with a BiDi isolate charatcter and a BiDi closing character. + * + * This helps with monetary values in RTL languages that display the + * currency symbol. + */ +gchar * +gnc_wrap_text_with_bidi_ltr_isolate (const char *text); + /* Initialization ***************************************************/ void gnc_ui_util_init (void); @@ -446,9 +468,9 @@ gchar * gnc_filter_text_for_currency_symbol (const gchar *incoming_text, const gchar *symbol); /** Returns the incoming text removed of currency symbol - * + * * @param comm commodity of entry if known - * + * * @param incoming_text The text to filter * * @param symbol return the symbol used