diff --git a/FL/Fl_Widget.H b/FL/Fl_Widget.H
index 71f6c21..efc61a5 100644
--- a/FL/Fl_Widget.H
+++ b/FL/Fl_Widget.H
@@ -842,6 +842,9 @@ public:
    */
   unsigned int visible_focus() { return flags_ & VISIBLE_FOCUS; }
 
+  static unsigned int fribidi();
+  static void fribidi(int v);
+
   /** The default callback for all widgets that don't set a callback.
 
     This callback function puts a pointer to the widget on the queue
diff --git a/src/Fl_Widget.cxx b/src/Fl_Widget.cxx
index 502fe35..ee98ac9 100644
--- a/src/Fl_Widget.cxx
+++ b/src/Fl_Widget.cxx
@@ -24,6 +24,146 @@
 #include <stdlib.h>
 #include "flstring.h"
 
+#if defined(FLTK_USE_FRIBIDI)
+
+#if defined(FLTK_DLOPEN_FRIBIDI)
+
+# include <dlfcn.h>
+# include <stdint.h>
+
+# if !defined(LIBFRIBIDI)
+#  define LIBFRIBIDI "libfribidi.so.0"
+# endif
+
+typedef int fribidi_boolean;
+typedef uint32_t FriBidiParType;
+typedef uint32_t FriBidiChar;
+typedef int FriBidiStrIndex;
+typedef signed char FriBidiLevel;
+
+typedef enum
+{
+  _FRIBIDI_CHAR_SET_NOT_FOUND,
+# define _FRIBIDI_ADD_CHAR_SET(CHAR_SET, char_set) FRIBIDI_CHAR_SET_##CHAR_SET,
+_FRIBIDI_ADD_CHAR_SET (UTF8, utf8)  /* UTF-8 (Unicode) */
+_FRIBIDI_ADD_CHAR_SET (CAP_RTL, cap_rtl)  /* CapRTL (Test) */
+_FRIBIDI_ADD_CHAR_SET (ISO8859_6, iso8859_6)  /* ISO8859-6 (Arabic) */
+_FRIBIDI_ADD_CHAR_SET (ISO8859_8, iso8859_8)  /* ISO8859-8 (Hebrew) */
+_FRIBIDI_ADD_CHAR_SET (CP1255, cp1255)  /* CP1255 (MS Hebrew/Yiddish) */
+_FRIBIDI_ADD_CHAR_SET (CP1256, cp1256)  /* CP1256 (MS Arabic) */
+# undef _FRIBIDI_ADD_CHAR_SET
+  _FRIBIDI_CHAR_SETS_NUM_PLUS_ONE
+}
+FriBidiCharSet;
+
+# define FRIBIDI_MASK_STRONG  0x00000010L
+# define FRIBIDI_MASK_LETTER  0x00000100L
+# define FRIBIDI_TYPE_LTR_VAL  ( FRIBIDI_MASK_STRONG | FRIBIDI_MASK_LETTER )
+# define FRIBIDI_PAR_LTR  FRIBIDI_TYPE_LTR_VAL
+
+#define FUNC(x) p_##x
+#define STRINGIFY(x) #x
+#define GETPROCADDR(handle,type,func,param) \
+  typedef type (*func##_t) param; \
+  func##_t p_##func = (func##_t) dlsym(handle, STRINGIFY(func));
+
+#else
+# include <fribidi.h>
+# define FUNC(x) x
+#endif  /* FLTK_DLOPEN_FRIBIDI */
+
+#define FRIBIDI_MAX_STRLEN 65000
+
+static unsigned int fribidi_ = 0;
+
+unsigned int Fl_Widget::fribidi(void) {
+  return fribidi_;
+}
+
+void Fl_Widget::fribidi(int v) {
+  fribidi_ = v ? 1 : 0;
+}
+
+/** Returns pointer to allocated string or NULL on error. */
+static char *fribidi_parse_line(const char *input)
+{
+  char buffer[FRIBIDI_MAX_STRLEN];
+  size_t size;
+  FriBidiParType base = FRIBIDI_PAR_LTR;
+  FriBidiStrIndex len, *ltov = NULL, *vtol = NULL;
+  FriBidiChar logical[FRIBIDI_MAX_STRLEN];
+  FriBidiChar visual[FRIBIDI_MAX_STRLEN];
+  FriBidiLevel *levels = NULL;
+  FriBidiCharSet charset;
+
+  if (!input || (size = strlen(input)) == 0)
+    return NULL;
+
+#if defined(FLTK_DLOPEN_FRIBIDI)
+  void *handle = dlopen(LIBFRIBIDI, RTLD_LAZY);
+
+  if (!handle)
+    return NULL;
+
+# define DLSYM(type,func,param) \
+  GETPROCADDR(handle,type,func,param) \
+  if (dlerror()) { dlclose(handle); return NULL; }
+
+  DLSYM( FriBidiCharSet, fribidi_parse_charset, (const char *) )
+  DLSYM( fribidi_boolean, fribidi_set_mirroring, (fribidi_boolean) )
+  DLSYM( fribidi_boolean, fribidi_set_reorder_nsm, (fribidi_boolean) )
+  DLSYM( FriBidiStrIndex, fribidi_charset_to_unicode, (FriBidiCharSet, const char *, FriBidiStrIndex, FriBidiChar *) )
+  DLSYM( FriBidiLevel, fribidi_log2vis, (const FriBidiChar *, const FriBidiStrIndex, FriBidiParType *, FriBidiChar *,
+                                         FriBidiStrIndex *, FriBidiStrIndex *, FriBidiLevel *) )
+  DLSYM( FriBidiStrIndex, fribidi_remove_bidi_marks, (FriBidiChar *, const FriBidiStrIndex, FriBidiStrIndex *,
+                                                      FriBidiStrIndex *, FriBidiLevel *) )
+  DLSYM( FriBidiStrIndex, fribidi_unicode_to_charset, (FriBidiCharSet, const FriBidiChar *, FriBidiStrIndex, char *) )
+
+# define DLCLOSE dlclose(handle);
+#else
+# define DLCLOSE /**/
+#endif  /* FLTK_DLOPEN_FRIBIDI */
+
+  charset = FUNC(fribidi_parse_charset) ("UTF-8");
+  FUNC(fribidi_set_mirroring) (true);
+  FUNC(fribidi_set_reorder_nsm) (false);
+
+  if (size >= FRIBIDI_MAX_STRLEN) {
+    size = FRIBIDI_MAX_STRLEN - 1;
+  }
+
+  strncpy(buffer, input, size);
+  len = FUNC(fribidi_charset_to_unicode) (charset, buffer, size, logical);
+
+  if (len == 0) {
+    DLCLOSE
+    return NULL;
+  }
+
+  if ( FUNC(fribidi_log2vis) (logical, len, &base, visual, ltov, vtol, levels) ) {
+    len = FUNC(fribidi_remove_bidi_marks) (visual, len, ltov, vtol, levels);
+    if (len > 0) {
+      FUNC(fribidi_unicode_to_charset) (charset, visual, len, buffer);
+      DLCLOSE
+      return strdup(buffer);
+    }
+  }
+
+  DLCLOSE
+  return NULL;
+}
+# undef DLCLOSE
+# undef DLSYM
+# undef FUNC
+
+#else
+
+/* no fribidi support */
+unsigned int Fl_Widget::fribidi(void) { return 0; }
+void Fl_Widget::fribidi(int) { }
+
+#endif
+
 
 ////////////////////////////////////////////////////////////////
 // for compatibility with Forms, all widgets without callbacks are
@@ -111,7 +251,17 @@ Fl_Widget::Fl_Widget(int X, int Y, int W, int H, const char* L) {
 
   x_ = X; y_ = Y; w_ = W; h_ = H;
 
-  label_.value	 = L;
+#if defined(FLTK_USE_FRIBIDI)
+  char *str;
+  if (L && fribidi() && (str = fribidi_parse_line(L))) {
+    label_.value = str;
+    set_flag(COPIED_LABEL);
+  } else {
+    label_.value = L;
+  }
+#else
+  label_.value   = L;
+#endif
   label_.image   = 0;
   label_.deimage = 0;
   label_.type	 = FL_NORMAL_LABEL;
@@ -283,7 +433,18 @@ void Fl_Widget::label(const char *a) {
     free((void *)(label_.value));
     clear_flag(COPIED_LABEL);
   }
+
+#if defined(FLTK_USE_FRIBIDI)
+  char *b = NULL;
+  if (fribidi() && (b = fribidi_parse_line(a))) {
+    label_.value=b;
+    set_flag(COPIED_LABEL);
+  } else {
+    label_.value=a;
+  }
+#else
   label_.value=a;
+#endif
   redraw_label();
 }
 
