emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] /srv/bzr/emacs/trunk r105208: Support bidi reordering of o


From: Eli Zaretskii
Subject: [Emacs-diffs] /srv/bzr/emacs/trunk r105208: Support bidi reordering of overlay and display strings.
Date: Thu, 14 Jul 2011 20:28:42 +0300
User-agent: Bazaar (2.3.1)

------------------------------------------------------------
revno: 105208 [merge]
fixes bug(s): http://debbugs.gnu.org/7616 http://debbugs.gnu.org/8133 
http://debbugs.gnu.org/8867
committer: Eli Zaretskii <address@hidden>
branch nick: trunk
timestamp: Thu 2011-07-14 20:28:42 +0300
message:
  Support bidi reordering of overlay and display strings.
  Fix bugs #7616, #8133, #8867.
  
   src/xdisp.c (compute_display_string_pos)
   (compute_display_string_end): Accept additional argument STRING.
   (init_iterator, reseat_1): Initialize bidi_it->string.s to NULL.
   (reseat_to_string): Initialize bidi_it->string.s and
   bidi_it->string.schars.
   (Fcurrent_bidi_paragraph_direction): Initialize itb.string.s to
   NULL (avoids a crash in bidi_paragraph_init).  Initialize
   itb.string.lstring.
   (init_iterator): Call bidi_init_it only of a valid
   buffer position was specified.  Initialize paragraph_embedding to
   L2R.
   (reseat_to_string): Initialize the bidi iterator.
   (display_string): If we need to ignore text properties of
   LISP_STRING, set IT->stop_charpos to IT->end_charpos.  (The
   original value of -1 will not work with bidi.)
   (compute_display_string_pos): First arg is now struct
   `text_pos *'; all callers changed.  Support display properties on
   Lisp strings.
   (compute_display_string_end): Support display properties on Lisp
   strings.
   (init_iterator, reseat_1, reseat_to_string): Initialize the
   string.bufpos member to 0 (zero, for compatibility with IT_CHARPOS
   when iterating on a string not from display properties).
   (compute_display_string_pos, compute_display_string_end): Fix
   calculation of the object to scan.  Fixes an error when using
   arrow keys.
   (next_element_from_buffer): Don't abort when IT_CHARPOS is before
   base_level_stop; instead, set base_level_stop to BEGV.  Fixes
   crashes in vertical-motion.
   (next_element_from_buffer): Improve commentary for when
   the iterator is before prev_stop.
   (init_iterator): Initialize bidi_p from the default value of
   bidi-display-reordering, not from buffer-local value.  Use the
   buffer-local value only if initializing for buffer iteration.
   (handle_invisible_prop): Support invisible properties on strings
   that are being bidi-reordered.
   (set_iterator_to_next): Support bidi reordering of C strings and
   Lisp strings.
   (next_element_from_string): Support bidi reordering of Lisp
   strings.
   (handle_stop_backwards): Support Lisp strings as well.
   (display_string): Support display of R2L glyph rows.  Use
   IT_STRING_CHARPOS when displaying from a Lisp string.
   (init_iterator): Don't initialize it->bidi_p for strings
   here.
   (reseat_to_string): Initialize it->bidi_p for strings here.
   (next_element_from_string, next_element_from_c_string)
   (next_element_from_buffer): Add xassert's for correspondence
   between IT's object being iterated and it->bidi_it.string
   structure.
   (face_before_or_after_it_pos): Support bidi iteration.
   (next_element_from_c_string): Handle the case of the first string
   character that is not the first one in the visual order.
   (get_visually_first_element): New function, refactored from common
   parts of next_element_from_buffer, next_element_from_string, and
   next_element_from_c_string.
   (tool_bar_lines_needed, redisplay_tool_bar)
   (display_menu_bar): Force left-to-right direction.  Add a FIXME
   comment for making that be controlled by a user option.
   (push_it, pop_it): Save and restore the state of the
   bidi iterator.  Save and restore the bidi_p flag.
   (pop_it): Iterate out of display property for string iteration as
   well.
   (iterate_out_of_display_property): Support iteration over strings.
   (handle_single_display_spec): Set up it->bidi_it for iteration
   over a display string, and call bidi_init_it.
   (handle_single_display_spec, next_overlay_string)
   (get_overlay_strings_1, push_display_prop): Set up the bidi
   iterator for displaying display or overlay strings.
   (forward_to_next_line_start): Don't use the shortcut if
   bidi-iterating.
   (back_to_previous_visible_line_start): If handle_display_prop
   pushed the iterator stack, restore the internal state of the bidi
   iterator by calling bidi_pop_it same number of times.
   (reseat_at_next_visible_line_start): If ON_NEWLINE_P is non-zero,
   and we are bidi-iterating, don't decrement the iterator position;
   instead, set the first_elt flag in the bidi iterator, to produce
   the same effect.
   (reseat_1): Remove redundant setting of string_from_display_prop_p.
   (push_display_prop): xassert that we are iterating a buffer.
   (push_it, pop_it): Save and restore paragraph_embedding member.
   (handle_single_display_spec, next_overlay_string)
   (get_overlay_strings_1, reseat_1, reseat_to_string)
   (push_display_prop): Set up the `unibyte' member of bidi_it.string
   correctly.  Don't assume unibyte strings are not bidi-reordered.
   (compute_display_string_pos)
   (compute_display_string_end): Fix handling the case of C string.
   (push_it, pop_it): Save and restore from_disp_prop_p.
   (handle_single_display_spec, push_display_prop): Set the
   from_disp_prop_p flag.
   (get_overlay_strings_1): Reset the from_disp_prop_p flag.
   (pop_it): Call iterate_out_of_display_property only if we are
   popping after iteration over a string that came from a display
   property.  Fix a typo in popping stretch info.  Add an assertion
   for verifying that the iterator position is in sync with the bidi
   iterator.
   (handle_single_display_spec, get_overlay_strings_1)
   (push_display_prop): Fix initialization of paragraph direction for
   string when that of the parent object is not yet determined.
   (reseat_1): Call bidi_init_it to resync the bidi
   iterator with IT's position.  (Bug#7616)
   (find_row_edges): If ROW->start.pos gives position
   smaller than min_pos, use it as ROW->minpos.  (Bug#7616)
   (handle_stop, back_to_previous_visible_line_start, reseat_1):
   Reset the from_disp_prop_p flag.
   (SAVE_IT, RESTORE_IT): New macros.
   (pos_visible_p, face_before_or_after_it_pos)
   (back_to_previous_visible_line_start)
   (move_it_in_display_line_to, move_it_in_display_line)
   (move_it_to, move_it_vertically_backward, move_it_by_lines)
   (try_scrolling, redisplay_window, display_line): Use them when
   saving a temporary copy of the iterator and restoring it back.
   (back_to_previous_visible_line_start, reseat_1)
   (init_iterator): Empty the bidi cache "stack".
   (move_it_in_display_line_to): If iterator ended up at
   EOL, but we never saw any buffer positions smaller than
   to_charpos, return MOVE_POS_MATCH_OR_ZV.  Fixes vertical cursor
   motion in bidi-reordered lines.
   (move_it_in_display_line_to): Record prev_method and prev_pos
   immediately before the call to set_iterator_to_next.  Fixes cursor
   motion in bidi-reordered lines with stretch glyphs and strings
   displayed in margins.  (Bug#8133) (Bug#8867)
   Return MOVE_POS_MATCH_OR_ZV only if iterator position is past
   TO_CHARPOS.
   (pos_visible_p): Support positions in bidi-reordered lines.  Save
   and restore bidi cache.
   src/bidi.c (bidi_level_of_next_char): clen should be EMACS_NT, not int.
   (bidi_paragraph_info): Delete unused struct.
   (bidi_cache_idx, bidi_cache_last_idx): Declare EMACS_INT.
   (bidi_cache_start): New variable.
   (bidi_cache_reset): Reset bidi_cache_idx to bidi_cache_start, not
   to zero.
   (bidi_cache_fetch_state, bidi_cache_search)
   (bidi_cache_find_level_change, bidi_cache_iterator_state)
   (bidi_cache_find, bidi_peek_at_next_level)
   (bidi_level_of_next_char, bidi_find_other_level_edge)
   (bidi_move_to_visually_next): Compare cache index with
   bidi_cache_start rather than with zero.
   (bidi_fetch_char): Accept new argument STRING; all callers
   changed.  Support iteration over a string.  Support strings with
   display properties.  Support unibyte strings.  Fix the type of
   `len' according to what STRING_CHAR_AND_LENGTH expects.
   (bidi_paragraph_init, bidi_resolve_explicit_1)
   (bidi_resolve_explicit, bidi_resolve_weak)
   (bidi_level_of_next_char, bidi_move_to_visually_next): Support
   iteration over a string.
   (bidi_set_sor_type, bidi_resolve_explicit_1)
   (bidi_resolve_explicit, bidi_type_of_next_char): ignore_bn_limit
   can now be zero (for strings); special values 0 and -1 were
   changed to -1 and -2, respectively.
   (bidi_char_at_pos): New function.
   (bidi_paragraph_init, bidi_resolve_explicit, bidi_resolve_weak):
   Call it instead of FETCH_MULTIBYTE_CHAR.
   (bidi_move_to_visually_next): Abort if charpos or bytepos were not
   initialized to valid values.
   (bidi_init_it): Don't initialize charpos and bytepos with invalid
   values.
   (bidi_level_of_next_char): Allow the sentinel "position" to pass
   the test for valid cached positions.  Fix the logic for looking up
   the sentinel state in the cache.  GCPRO the Lisp string we are
   iterating.
   (bidi_push_it, bidi_pop_it): New functions.
   (bidi_initialize): Initialize the bidi cache start stack pointer.
   (bidi_cache_ensure_space): New function, refactored from part of
   bidi_cache_iterator_state.  Don't assume the required size is just
   one BIDI_CACHE_CHUNK away.
   (bidi_cache_start_stack, bidi_push_it): Use IT_STACK_SIZE.
   (bidi_count_bytes, bidi_char_at_pos): New functions.
   (bidi_cache_search): Don't assume bidi_cache_last_idx is
   always valid if bidi_cache_idx is valid.
   (bidi_cache_find_level_change): xassert that bidi_cache_last_idx
   is valid if it's going to be used.
   (bidi_shelve_cache, bidi_unshelve_cache): New functions.
   (bidi_cache_fetch_state, bidi_cache_search)
   (bidi_cache_find_level_change, bidi_cache_ensure_space)
   (bidi_cache_iterator_state, bidi_cache_find)
   (bidi_find_other_level_edge, bidi_cache_start_stack): All
   variables related to cache indices are now EMACS_INT.
   src/dispextern.h (struct bidi_string_data): New structure.
   (struct bidi_it): New member `string'.  Make flag members be 1-bit
   fields, and put them last in the struct.
   (compute_display_string_pos, compute_display_string_end): Update
   prototypes.
   (bidi_push_it, bidi_pop_it): Add prototypes.
   (struct iterator_stack_entry): New members bidi_p,
   paragraph_embedding, and from_disp_prop_p.
   (struct it): Member bidi_p is now a bit field 1 bit wide.
   (bidi_shelve_cache, bidi_unshelve_cache): Declare
   prototypes.
   src/.gdbinit (xvectype, xvector, xcompiled, xchartable, xboolvector)
   (xpr, xfont, xbacktrace): Use "header.size" when accessing vectors
   and vector-like objects.
   src/dispnew.c (buffer_posn_from_coords): Save and restore the bidi
   cache around display iteration.
   src/window.c (Fwindow_end, window_scroll_pixel_based)
   (displayed_window_lines, Frecenter): Save and restore the bidi
   cache around display iteration.
   lisp/buff-menu.el (Buffer-menu-buffer+size): Accept an additional
   argument LRM; if non-nil, append an invisible LRM character to the
   buffer name.
   (list-buffers-noselect): Call Buffer-menu-buffer+size with the
   last argument non-nil, when formatting buffer names.
   (Buffer-menu-mode, list-buffers-noselect): Force left-to-right
   paragraph direction.
   doc/lispref/display.texi (Other Display Specs): Document that `left-fringe'
   and `right-fringe' display specifications are of the "replacing"
   kind.
modified:
  doc/lispref/ChangeLog
  doc/lispref/display.texi
  etc/NEWS
  etc/TODO
  lisp/ChangeLog
  lisp/buff-menu.el
  src/.gdbinit
  src/ChangeLog
  src/bidi.c
  src/dispextern.h
  src/dispnew.c
  src/indent.c
  src/window.c
  src/xdisp.c
=== modified file 'doc/lispref/ChangeLog'
--- a/doc/lispref/ChangeLog     2011-07-14 00:02:09 +0000
+++ b/doc/lispref/ChangeLog     2011-07-14 17:28:42 +0000
@@ -1,3 +1,9 @@
+2011-07-14  Eli Zaretskii  <address@hidden>
+
+       * display.texi (Other Display Specs): Document that `left-fringe'
+       and `right-fringe' display specifications are of the "replacing"
+       kind.
+
 2011-07-14  Lars Magne Ingebrigtsen  <address@hidden>
 
        * help.texi (Documentation Basics): Add a link to the Function

=== modified file 'doc/lispref/display.texi'
--- a/doc/lispref/display.texi  2011-07-12 22:09:28 +0000
+++ b/doc/lispref/display.texi  2011-07-14 17:28:42 +0000
@@ -3899,7 +3899,8 @@
 @itemx (right-fringe @var{bitmap} @address@hidden@r{]})
 This display specification on any character of a line of text causes
 the specified @var{bitmap} be displayed in the left or right fringes
-for that line.  The optional @var{face} specifies the colors to be
+for that line, instead of the characters that have the display
+specification.  The optional @var{face} specifies the colors to be
 used for the bitmap.  @xref{Fringe Bitmaps}, for the details.
 
 @item (space-width @var{factor})

=== modified file 'etc/NEWS'
--- a/etc/NEWS  2011-07-13 22:00:48 +0000
+++ b/etc/NEWS  2011-07-14 17:28:42 +0000
@@ -171,7 +171,7 @@
 *** Emacs now supports display and editing of bidirectional text.
 
 See the node "Bidirectional Editing" in the Emacs Manual for some
-initial documentation.
+additional documentation.
 
 To turn this on in any given buffer, set the buffer-local variable
 `bidi-display-reordering' to a non-nil value.  The default is nil.
@@ -190,10 +190,6 @@
 bidirectionality" class implementation of the Unicode Bidirectional
 Algorithm.
 
-Note that some advanced display features, such as overlay strings and
-`display' text properties, do not yet work correctly when
-bidirectional text is reordered for display.
-
 +++
 *** Enhanced support for characters that have no glyphs in available fonts.
 If a character has no glyphs in any of the available fonts, Emacs by

=== modified file 'etc/TODO'
--- a/etc/TODO  2011-06-30 02:56:12 +0000
+++ b/etc/TODO  2011-07-14 17:28:42 +0000
@@ -639,6 +639,37 @@
 
 **** Undo for color-drag face customization.
 
+** Bidirectional editing
+
+*** Allow the user to control the direction of the UI
+
+**** Introduce user option to control direction of mode line.
+This requires to figure out what to do with unibyte strings that are
+used in constructing the mode line.  Currently, unibyte strings are
+not reordered by bidi.c, without which R2L mode line will not display
+correctly.  One possibility would be to STRING_SET_MULTIBYTE all Lisp
+strings involved in the mode line, and then pass them through bidi.c.
+
+Another problem is the header line, which is produced by the same
+routines as the mode line.  While it makes sense to have the mode-line
+direction controlled by a single global variable, header lines are
+buffer-specific, so they need a separate treatment in this regard.
+
+**** User options to control direction of menu bar and tool bar.
+For the tool bar, it's relatively easy: set it.paragraph_embedding
+in redisplay_tool_bar according to the user variable, and make
+f->desired_tool_bar_string multibyte with STRING_SET_MULTIBYTE.  Some
+minor changes will be needed to set the right_box_line_p and
+left_box_line_p flags correctly for the R2L tool bar.
+
+However, it makes no sense to display the tool bar right to left if
+the menu bar cannot be displayed in the same direction.
+
+R2L menu bar is tricky for the same reasons as the mode line.  In
+addition, toolkit builds create their menu bars in toolkit-specific
+parts of code, bypassing xdisp.c, so those parts need to be enhanced
+with toolkit-specific code to display the menu bar right to left.
+
 ** ImageMagick support
 
 *** image-type-header-regexps priorities the jpeg loader over the
@@ -663,8 +694,8 @@
 
 *** Integrate with image-dired.
 
-*** Integrate with docview. 
-  
+*** Integrate with docview.
+
 *** Integrate with image-mode.
 Some work has been done, e.g. M-x image-transform-fit-to-height will
 fit the image to the height of the Emacs window.

=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog    2011-07-14 17:20:37 +0000
+++ b/lisp/ChangeLog    2011-07-14 17:28:42 +0000
@@ -1,3 +1,13 @@
+2011-07-14  Eli Zaretskii  <address@hidden>
+
+       * buff-menu.el (Buffer-menu-buffer+size): Accept an additional
+       argument LRM; if non-nil, append an invisible LRM character to the
+       buffer name.
+       (list-buffers-noselect): Call Buffer-menu-buffer+size with the
+       last argument non-nil, when formatting buffer names.
+       (Buffer-menu-mode, list-buffers-noselect): Force left-to-right
+       paragraph direction.
+
 2011-07-14  Lars Magne Ingebrigtsen  <address@hidden>
 
        * image.el (put-image): Mention the `put-image' overlay property

=== modified file 'lisp/buff-menu.el'
--- a/lisp/buff-menu.el 2011-05-28 20:08:05 +0000
+++ b/lisp/buff-menu.el 2011-07-14 17:28:42 +0000
@@ -266,7 +266,10 @@
   (set (make-local-variable 'buffer-stale-function)
        (lambda (&optional _noconfirm) 'fast))
   (setq truncate-lines t)
-  (setq buffer-read-only t))
+  (setq buffer-read-only t)
+  ;; Force L2R direction, to avoid messing the display if the first
+  ;; buffer in the list happens to begin with a strong R2L character.
+  (setq bidi-paragraph-direction 'left-to-right))
 
 (define-obsolete-variable-alias 'buffer-menu-mode-hook
   'Buffer-menu-mode-hook "23.1")
@@ -663,7 +666,7 @@
   ":" ;; (if (char-displayable-p ?…) "…" ":")
   )
 
-(defun Buffer-menu-buffer+size (name size &optional name-props size-props)
+(defun Buffer-menu-buffer+size (name size &optional name-props size-props lrm)
   (if (> (+ (string-width name) (string-width size) 2)
          Buffer-menu-buffer+size-width)
       (setq name
@@ -678,9 +681,17 @@
                           (string-width tail)
                           2))
                       Buffer-menu-short-ellipsis
-                      tail)))
+                      tail
+                     ;; Append an invisible LRM character to the
+                     ;; buffer's name to avoid ugly display with the
+                     ;; buffer size to the left of the name, when the
+                     ;; name begins with R2L character.
+                     (if lrm (propertize (string ?\x200e) 'invisible t) ""))))
     ;; Don't put properties on (buffer-name).
-    (setq name (copy-sequence name)))
+    (setq name (concat (copy-sequence name)
+                      (if lrm
+                          (propertize (string ?\x200e) 'invisible t)
+                        ""))))
   (add-text-properties 0 (length name) name-props name)
   (add-text-properties 0 (length size) size-props size)
   (let ((name+space-width (- Buffer-menu-buffer+size-width
@@ -813,6 +824,10 @@
       (setq buffer-read-only nil)
       (erase-buffer)
       (setq standard-output (current-buffer))
+      ;; Force L2R direction, to avoid messing the display if the
+      ;; first buffer in the list happens to begin with a strong R2L
+      ;; character.
+      (setq bidi-paragraph-direction 'left-to-right)
       (unless Buffer-menu-use-header-line
         ;; Use U+2014 (EM DASH) to underline if possible, else use ASCII
         ;; (i.e. U+002D, HYPHEN-MINUS).
@@ -914,7 +929,8 @@
                                                   (max (length size) 3)
                                                   2))
                                            name
-                                         "mouse-2: select this buffer"))))
+                                         "mouse-2: select this buffer"))
+                        nil t))
                "  "
                (if (> (string-width (nth 4 buffer)) Buffer-menu-mode-width)
                    (truncate-string-to-width (nth 4 buffer)

=== modified file 'src/.gdbinit'
--- a/src/.gdbinit      2011-03-11 00:19:57 +0000
+++ b/src/.gdbinit      2011-06-18 13:28:53 +0000
@@ -677,7 +677,7 @@
 
 define xvectype
   xgetptr $
-  set $size = ((struct Lisp_Vector *) $ptr)->size
+  set $size = ((struct Lisp_Vector *) $ptr)->header.size
   output ($size & PVEC_FLAG) ? (enum pvec_type) ($size & PVEC_TYPE_MASK) : 
$size & ~gdb_array_mark_flag
   echo \n
 end
@@ -818,7 +818,7 @@
 define xvector
   xgetptr $
   print (struct Lisp_Vector *) $ptr
-  output ($->size > 50) ? 0 : ($->contents[0])@($->size & ~gdb_array_mark_flag)
+  output ($->header.size > 50) ? 0 : ($->contents[0])@($->header.size & 
~gdb_array_mark_flag)
 echo \n
 end
 document xvector
@@ -853,7 +853,7 @@
 define xcompiled
   xgetptr $
   print (struct Lisp_Vector *) $ptr
-  output ($->contents[0])@($->size & 0xff)
+  output ($->contents[0])@($->header.size & 0xff)
 end
 document xcompiled
 Print $ as a compiled function pointer.
@@ -903,7 +903,7 @@
   print (struct Lisp_Char_Table *) $ptr
   printf "Purpose: "
   xprintsym $->purpose
-  printf "  %d extra slots", ($->size & 0x1ff) - 68
+  printf "  %d extra slots", ($->header.size & 0x1ff) - 68
   echo \n
 end
 document xchartable
@@ -927,7 +927,7 @@
 define xboolvector
   xgetptr $
   print (struct Lisp_Bool_Vector *) $ptr
-  output ($->size > 256) ? 0 : ($->data[0])@((($->size & ~gdb_array_mark_flag) 
+ 7)/ 8)
+  output ($->header.size > 256) ? 0 : ($->data[0])@((($->header.size & 
~gdb_array_mark_flag) + 7)/ 8)
   echo \n
 end
 document xboolvector
@@ -1093,7 +1093,7 @@
 #    end
   end
   if $type == Lisp_Vectorlike
-    set $size = ((struct Lisp_Vector *) $ptr)->size
+    set $size = ((struct Lisp_Vector *) $ptr)->header.size
     if ($size & PVEC_FLAG)
       set $vec = (enum pvec_type) ($size & PVEC_TYPE_MASK)
       if $vec == PVEC_NORMAL_VECTOR
@@ -1202,7 +1202,7 @@
 
 define xfont
   xgetptr $
-  set $size = (((struct Lisp_Vector *) $ptr)->size & 0x1FF)
+  set $size = (((struct Lisp_Vector *) $ptr)->header.size & 0x1FF)
   if $size == FONT_SPEC_MAX
     print (struct font_spec *) $ptr
   else
@@ -1229,7 +1229,7 @@
       printf "0x%x ", $ptr
       if $type == Lisp_Vectorlike
        xgetptr (*$bt->function)
-        set $size = ((struct Lisp_Vector *) $ptr)->size
+        set $size = ((struct Lisp_Vector *) $ptr)->header.size
         output ($size & PVEC_FLAG) ? (enum pvec_type) ($size & PVEC_TYPE_MASK) 
: $size & ~gdb_array_mark_flag
       else
         printf "Lisp type %d", $type

=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2011-07-14 13:37:20 +0000
+++ b/src/ChangeLog     2011-07-14 17:28:42 +0000
@@ -1,3 +1,209 @@
+2011-07-14  Eli Zaretskii  <address@hidden>
+
+       Support bidi reordering of display and overlay strings.
+       * xdisp.c (compute_display_string_pos)
+       (compute_display_string_end): Accept additional argument STRING.
+       (init_iterator, reseat_1): Initialize bidi_it->string.s to NULL.
+       (reseat_to_string): Initialize bidi_it->string.s and
+       bidi_it->string.schars.
+       (Fcurrent_bidi_paragraph_direction): Initialize itb.string.s to
+       NULL (avoids a crash in bidi_paragraph_init).  Initialize
+       itb.string.lstring.
+       (init_iterator): Call bidi_init_it only of a valid
+       buffer position was specified.  Initialize paragraph_embedding to
+       L2R.
+       (reseat_to_string): Initialize the bidi iterator.
+       (display_string): If we need to ignore text properties of
+       LISP_STRING, set IT->stop_charpos to IT->end_charpos.  (The
+       original value of -1 will not work with bidi.)
+       (compute_display_string_pos): First arg is now struct
+       `text_pos *'; all callers changed.  Support display properties on
+       Lisp strings.
+       (compute_display_string_end): Support display properties on Lisp
+       strings.
+       (init_iterator, reseat_1, reseat_to_string): Initialize the
+       string.bufpos member to 0 (zero, for compatibility with IT_CHARPOS
+       when iterating on a string not from display properties).
+       (compute_display_string_pos, compute_display_string_end): Fix
+       calculation of the object to scan.  Fixes an error when using
+       arrow keys.
+       (next_element_from_buffer): Don't abort when IT_CHARPOS is before
+       base_level_stop; instead, set base_level_stop to BEGV.  Fixes
+       crashes in vertical-motion.
+       (next_element_from_buffer): Improve commentary for when
+       the iterator is before prev_stop.
+       (init_iterator): Initialize bidi_p from the default value of
+       bidi-display-reordering, not from buffer-local value.  Use the
+       buffer-local value only if initializing for buffer iteration.
+       (handle_invisible_prop): Support invisible properties on strings
+       that are being bidi-reordered.
+       (set_iterator_to_next): Support bidi reordering of C strings and
+       Lisp strings.
+       (next_element_from_string): Support bidi reordering of Lisp
+       strings.
+       (handle_stop_backwards): Support Lisp strings as well.
+       (display_string): Support display of R2L glyph rows.  Use
+       IT_STRING_CHARPOS when displaying from a Lisp string.
+       (init_iterator): Don't initialize it->bidi_p for strings
+       here.
+       (reseat_to_string): Initialize it->bidi_p for strings here.
+       (next_element_from_string, next_element_from_c_string)
+       (next_element_from_buffer): Add xassert's for correspondence
+       between IT's object being iterated and it->bidi_it.string
+       structure.
+       (face_before_or_after_it_pos): Support bidi iteration.
+       (next_element_from_c_string): Handle the case of the first string
+       character that is not the first one in the visual order.
+       (get_visually_first_element): New function, refactored from common
+       parts of next_element_from_buffer, next_element_from_string, and
+       next_element_from_c_string.
+       (tool_bar_lines_needed, redisplay_tool_bar)
+       (display_menu_bar): Force left-to-right direction.  Add a FIXME
+       comment for making that be controlled by a user option.
+       (push_it, pop_it): Save and restore the state of the
+       bidi iterator.  Save and restore the bidi_p flag.
+       (pop_it): Iterate out of display property for string iteration as
+       well.
+       (iterate_out_of_display_property): Support iteration over strings.
+       (handle_single_display_spec): Set up it->bidi_it for iteration
+       over a display string, and call bidi_init_it.
+       (handle_single_display_spec, next_overlay_string)
+       (get_overlay_strings_1, push_display_prop): Set up the bidi
+       iterator for displaying display or overlay strings.
+       (forward_to_next_line_start): Don't use the shortcut if
+       bidi-iterating.
+       (back_to_previous_visible_line_start): If handle_display_prop
+       pushed the iterator stack, restore the internal state of the bidi
+       iterator by calling bidi_pop_it same number of times.
+       (reseat_at_next_visible_line_start): If ON_NEWLINE_P is non-zero,
+       and we are bidi-iterating, don't decrement the iterator position;
+       instead, set the first_elt flag in the bidi iterator, to produce
+       the same effect.
+       (reseat_1): Remove redundant setting of string_from_display_prop_p.
+       (push_display_prop): xassert that we are iterating a buffer.
+       (push_it, pop_it): Save and restore paragraph_embedding member.
+       (handle_single_display_spec, next_overlay_string)
+       (get_overlay_strings_1, reseat_1, reseat_to_string)
+       (push_display_prop): Set up the `unibyte' member of bidi_it.string
+       correctly.  Don't assume unibyte strings are not bidi-reordered.
+       (compute_display_string_pos)
+       (compute_display_string_end): Fix handling the case of C string.
+       (push_it, pop_it): Save and restore from_disp_prop_p.
+       (handle_single_display_spec, push_display_prop): Set the
+       from_disp_prop_p flag.
+       (get_overlay_strings_1): Reset the from_disp_prop_p flag.
+       (pop_it): Call iterate_out_of_display_property only if we are
+       popping after iteration over a string that came from a display
+       property.  Fix a typo in popping stretch info.  Add an assertion
+       for verifying that the iterator position is in sync with the bidi
+       iterator.
+       (handle_single_display_spec, get_overlay_strings_1)
+       (push_display_prop): Fix initialization of paragraph direction for
+       string when that of the parent object is not yet determined.
+       (reseat_1): Call bidi_init_it to resync the bidi
+       iterator with IT's position.  (Bug#7616)
+       (find_row_edges): If ROW->start.pos gives position
+       smaller than min_pos, use it as ROW->minpos.  (Bug#7616)
+       (handle_stop, back_to_previous_visible_line_start, reseat_1):
+       Reset the from_disp_prop_p flag.
+       (SAVE_IT, RESTORE_IT): New macros.
+       (pos_visible_p, face_before_or_after_it_pos)
+       (back_to_previous_visible_line_start)
+       (move_it_in_display_line_to, move_it_in_display_line)
+       (move_it_to, move_it_vertically_backward, move_it_by_lines)
+       (try_scrolling, redisplay_window, display_line): Use them when
+       saving a temporary copy of the iterator and restoring it back.
+       (back_to_previous_visible_line_start, reseat_1)
+       (init_iterator): Empty the bidi cache "stack".
+       (move_it_in_display_line_to): If iterator ended up at
+       EOL, but we never saw any buffer positions smaller than
+       to_charpos, return MOVE_POS_MATCH_OR_ZV.  Fixes vertical cursor
+       motion in bidi-reordered lines.
+       (move_it_in_display_line_to): Record prev_method and prev_pos
+       immediately before the call to set_iterator_to_next.  Fixes cursor
+       motion in bidi-reordered lines with stretch glyphs and strings
+       displayed in margins.  (Bug#8133) (Bug#8867)
+       Return MOVE_POS_MATCH_OR_ZV only if iterator position is past
+       TO_CHARPOS.
+       (pos_visible_p): Support positions in bidi-reordered lines.  Save
+       and restore bidi cache.
+
+       * bidi.c (bidi_level_of_next_char): clen should be EMACS_NT, not int.
+       (bidi_paragraph_info): Delete unused struct.
+       (bidi_cache_idx, bidi_cache_last_idx): Declare EMACS_INT.
+       (bidi_cache_start): New variable.
+       (bidi_cache_reset): Reset bidi_cache_idx to bidi_cache_start, not
+       to zero.
+       (bidi_cache_fetch_state, bidi_cache_search)
+       (bidi_cache_find_level_change, bidi_cache_iterator_state)
+       (bidi_cache_find, bidi_peek_at_next_level)
+       (bidi_level_of_next_char, bidi_find_other_level_edge)
+       (bidi_move_to_visually_next): Compare cache index with
+       bidi_cache_start rather than with zero.
+       (bidi_fetch_char): Accept new argument STRING; all callers
+       changed.  Support iteration over a string.  Support strings with
+       display properties.  Support unibyte strings.  Fix the type of
+       `len' according to what STRING_CHAR_AND_LENGTH expects.
+       (bidi_paragraph_init, bidi_resolve_explicit_1)
+       (bidi_resolve_explicit, bidi_resolve_weak)
+       (bidi_level_of_next_char, bidi_move_to_visually_next): Support
+       iteration over a string.
+       (bidi_set_sor_type, bidi_resolve_explicit_1)
+       (bidi_resolve_explicit, bidi_type_of_next_char): ignore_bn_limit
+       can now be zero (for strings); special values 0 and -1 were
+       changed to -1 and -2, respectively.
+       (bidi_char_at_pos): New function.
+       (bidi_paragraph_init, bidi_resolve_explicit, bidi_resolve_weak):
+       Call it instead of FETCH_MULTIBYTE_CHAR.
+       (bidi_move_to_visually_next): Abort if charpos or bytepos were not
+       initialized to valid values.
+       (bidi_init_it): Don't initialize charpos and bytepos with invalid
+       values.
+       (bidi_level_of_next_char): Allow the sentinel "position" to pass
+       the test for valid cached positions.  Fix the logic for looking up
+       the sentinel state in the cache.  GCPRO the Lisp string we are
+       iterating.
+       (bidi_push_it, bidi_pop_it): New functions.
+       (bidi_initialize): Initialize the bidi cache start stack pointer.
+       (bidi_cache_ensure_space): New function, refactored from part of
+       bidi_cache_iterator_state.  Don't assume the required size is just
+       one BIDI_CACHE_CHUNK away.
+       (bidi_cache_start_stack, bidi_push_it): Use IT_STACK_SIZE.
+       (bidi_count_bytes, bidi_char_at_pos): New functions.
+       (bidi_cache_search): Don't assume bidi_cache_last_idx is
+       always valid if bidi_cache_idx is valid.
+       (bidi_cache_find_level_change): xassert that bidi_cache_last_idx
+       is valid if it's going to be used.
+       (bidi_shelve_cache, bidi_unshelve_cache): New functions.
+       (bidi_cache_fetch_state, bidi_cache_search)
+       (bidi_cache_find_level_change, bidi_cache_ensure_space)
+       (bidi_cache_iterator_state, bidi_cache_find)
+       (bidi_find_other_level_edge, bidi_cache_start_stack): All
+       variables related to cache indices are now EMACS_INT.
+
+       * dispextern.h (struct bidi_string_data): New structure.
+       (struct bidi_it): New member `string'.  Make flag members be 1-bit
+       fields, and put them last in the struct.
+       (compute_display_string_pos, compute_display_string_end): Update
+       prototypes.
+       (bidi_push_it, bidi_pop_it): Add prototypes.
+       (struct iterator_stack_entry): New members bidi_p,
+       paragraph_embedding, and from_disp_prop_p.
+       (struct it): Member bidi_p is now a bit field 1 bit wide.
+       (bidi_shelve_cache, bidi_unshelve_cache): Declare
+       prototypes.
+
+       * .gdbinit (xvectype, xvector, xcompiled, xchartable, xboolvector)
+       (xpr, xfont, xbacktrace): Use "header.size" when accessing vectors
+       and vector-like objects.
+
+       * dispnew.c (buffer_posn_from_coords): Save and restore the bidi
+       cache around display iteration.
+
+       * window.c (Fwindow_end, window_scroll_pixel_based)
+       (displayed_window_lines, Frecenter): Save and restore the bidi
+       cache around display iteration.
+
 2011-07-14  Lars Magne Ingebrigtsen  <address@hidden>
 
        * editfns.c (Fdelete_region): Clarify the use of the named

=== modified file 'src/bidi.c'
--- a/src/bidi.c        2011-06-26 17:23:40 +0000
+++ b/src/bidi.c        2011-07-14 17:28:42 +0000
@@ -1,4 +1,4 @@
-/* Low-level bidirectional buffer-scanning functions for GNU Emacs.
+/* Low-level bidirectional buffer/string-scanning functions for GNU Emacs.
    Copyright (C) 2000-2001, 2004-2005, 2009-2011
    Free Software Foundation, Inc.
 
@@ -20,7 +20,7 @@
 /* Written by Eli Zaretskii <address@hidden>.
 
    A sequential implementation of the Unicode Bidirectional algorithm,
-   as per UAX#9, a part of the Unicode Standard.
+   (UBA) as per UAX#9, a part of the Unicode Standard.
 
    Unlike the reference and most other implementations, this one is
    designed to be called once for every character in the buffer or
@@ -35,11 +35,16 @@
    details about its algorithm that finds the next visual-order
    character by resolving their levels on the fly.
 
-   The two other entry points are bidi_paragraph_init and
+   Two other entry points are bidi_paragraph_init and
    bidi_mirror_char.  The first determines the base direction of a
    paragraph, while the second returns the mirrored version of its
    argument character.
 
+   A few auxiliary entry points are used to initialize the bidi
+   iterator for iterating an object (buffer or string), push and pop
+   the bidi iterator state, and save and restore the state of the bidi
+   cache.
+
    If you want to understand the code, you will have to read it
    together with the relevant portions of UAX#9.  The comments include
    references to UAX#9 rules, for that very reason.
@@ -66,16 +71,6 @@
 #define RLM_CHAR   0x200F
 #define BIDI_EOB   -1
 
-/* Local data structures.  (Look in dispextern.h for the rest.)  */
-
-/* What we need to know about the current paragraph.  */
-struct bidi_paragraph_info {
-  EMACS_INT start_bytepos;     /* byte position where it begins */
-  EMACS_INT end_bytepos;       /* byte position where it ends */
-  int      embedding_level;    /* its basic embedding level */
-  bidi_dir_t base_dir;         /* its base direction */
-};
-
 /* Data type for describing the bidirectional character categories.  */
 typedef enum {
   UNKNOWN_BC,
@@ -90,43 +85,10 @@
 static Lisp_Object paragraph_start_re, paragraph_separate_re;
 static Lisp_Object Qparagraph_start, Qparagraph_separate;
 
-static void
-bidi_initialize (void)
-{
-
-#include "biditype.h"
-#include "bidimirror.h"
-
-  int i;
-
-  bidi_type_table = Fmake_char_table (Qnil, make_number (STRONG_L));
-  staticpro (&bidi_type_table);
-
-  for (i = 0; i < sizeof bidi_type / sizeof bidi_type[0]; i++)
-    char_table_set_range (bidi_type_table, bidi_type[i].from, bidi_type[i].to,
-                         make_number (bidi_type[i].type));
-
-  bidi_mirror_table = Fmake_char_table (Qnil, Qnil);
-  staticpro (&bidi_mirror_table);
-
-  for (i = 0; i < sizeof bidi_mirror / sizeof bidi_mirror[0]; i++)
-    char_table_set (bidi_mirror_table, bidi_mirror[i].from,
-                   make_number (bidi_mirror[i].to));
-
-  Qparagraph_start = intern ("paragraph-start");
-  staticpro (&Qparagraph_start);
-  paragraph_start_re = Fsymbol_value (Qparagraph_start);
-  if (!STRINGP (paragraph_start_re))
-    paragraph_start_re = build_string ("\f\\|[ \t]*$");
-  staticpro (&paragraph_start_re);
-  Qparagraph_separate = intern ("paragraph-separate");
-  staticpro (&Qparagraph_separate);
-  paragraph_separate_re = Fsymbol_value (Qparagraph_separate);
-  if (!STRINGP (paragraph_separate_re))
-    paragraph_separate_re = build_string ("[ \t\f]*$");
-  staticpro (&paragraph_separate_re);
-  bidi_initialized = 1;
-}
+
+/***********************************************************************
+                       Utilities
+ ***********************************************************************/
 
 /* Return the bidi type of a character CH, subject to the current
    directional OVERRIDE.  */
@@ -243,6 +205,77 @@
   return c;
 }
 
+/* Determine the start-of-run (sor) directional type given the two
+   embedding levels on either side of the run boundary.  Also, update
+   the saved info about previously seen characters, since that info is
+   generally valid for a single level run.  */
+static inline void
+bidi_set_sor_type (struct bidi_it *bidi_it, int level_before, int level_after)
+{
+  int higher_level = level_before > level_after ? level_before : level_after;
+
+  /* The prev_was_pdf gork is required for when we have several PDFs
+     in a row.  In that case, we want to compute the sor type for the
+     next level run only once: when we see the first PDF.  That's
+     because the sor type depends only on the higher of the two levels
+     that we find on the two sides of the level boundary (see UAX#9,
+     clause X10), and so we don't need to know the final embedding
+     level to which we descend after processing all the PDFs.  */
+  if (!bidi_it->prev_was_pdf || level_before < level_after)
+    /* FIXME: should the default sor direction be user selectable?  */
+    bidi_it->sor = (higher_level & 1) != 0 ? R2L : L2R;
+  if (level_before > level_after)
+    bidi_it->prev_was_pdf = 1;
+
+  bidi_it->prev.type = UNKNOWN_BT;
+  bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1 =
+    bidi_it->last_strong.orig_type = UNKNOWN_BT;
+  bidi_it->prev_for_neutral.type = bidi_it->sor == R2L ? STRONG_R : STRONG_L;
+  bidi_it->prev_for_neutral.charpos = bidi_it->charpos;
+  bidi_it->prev_for_neutral.bytepos = bidi_it->bytepos;
+  bidi_it->next_for_neutral.type = bidi_it->next_for_neutral.type_after_w1 =
+    bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
+  bidi_it->ignore_bn_limit = -1; /* meaning it's unknown */
+}
+
+/* Push the current embedding level and override status; reset the
+   current level to LEVEL and the current override status to OVERRIDE.  */
+static inline void
+bidi_push_embedding_level (struct bidi_it *bidi_it,
+                          int level, bidi_dir_t override)
+{
+  bidi_it->stack_idx++;
+  xassert (bidi_it->stack_idx < BIDI_MAXLEVEL);
+  bidi_it->level_stack[bidi_it->stack_idx].level = level;
+  bidi_it->level_stack[bidi_it->stack_idx].override = override;
+}
+
+/* Pop the embedding level and directional override status from the
+   stack, and return the new level.  */
+static inline int
+bidi_pop_embedding_level (struct bidi_it *bidi_it)
+{
+  /* UAX#9 says to ignore invalid PDFs.  */
+  if (bidi_it->stack_idx > 0)
+    bidi_it->stack_idx--;
+  return bidi_it->level_stack[bidi_it->stack_idx].level;
+}
+
+/* Record in SAVED_INFO the information about the current character.  */
+static inline void
+bidi_remember_char (struct bidi_saved_info *saved_info,
+                   struct bidi_it *bidi_it)
+{
+  saved_info->charpos = bidi_it->charpos;
+  saved_info->bytepos = bidi_it->bytepos;
+  saved_info->type = bidi_it->type;
+  bidi_check_type (bidi_it->type);
+  saved_info->type_after_w1 = bidi_it->type_after_w1;
+  bidi_check_type (bidi_it->type_after_w1);
+  saved_info->orig_type = bidi_it->orig_type;
+  bidi_check_type (bidi_it->orig_type);
+}
+
 /* Copy the bidi iterator from FROM to TO.  To save cycles, this only
    copies the part of the level stack that is actually in use.  */
 static inline void
@@ -259,22 +292,37 @@
     to->level_stack[i] = from->level_stack[i];
 }
 
-/* Caching the bidi iterator states.  */
+
+/***********************************************************************
+                       Caching the bidi iterator states
+ ***********************************************************************/
 
 #define BIDI_CACHE_CHUNK 200
 static struct bidi_it *bidi_cache;
 static size_t bidi_cache_size = 0;
 static size_t elsz = sizeof (struct bidi_it);
-static int bidi_cache_idx;     /* next unused cache slot */
-static int bidi_cache_last_idx;        /* slot of last cache hit */
+static EMACS_INT bidi_cache_idx;       /* next unused cache slot */
+static EMACS_INT bidi_cache_last_idx;  /* slot of last cache hit */
+static EMACS_INT bidi_cache_start = 0; /* start of cache for this
+                                          "stack" level */
 
+/* Reset the cache state to the empty state.  We only reset the part
+   of the cache relevant to iteration of the current object.  Previous
+   objects, which are pushed on the display iterator's stack, are left
+   intact.  This is called when the cached information is no more
+   useful for the current iteration, e.g. when we were reseated to a
+   new position on the same object.  */
 static inline void
 bidi_cache_reset (void)
 {
-  bidi_cache_idx = 0;
+  bidi_cache_idx = bidi_cache_start;
   bidi_cache_last_idx = -1;
 }
 
+/* Shrink the cache to its minimal size.  Called when we init the bidi
+   iterator for reordering a buffer or a string that does not come
+   from display properties, because that means all the previously
+   cached info is of no further use.  */
 static inline void
 bidi_cache_shrink (void)
 {
@@ -288,11 +336,11 @@
 }
 
 static inline void
-bidi_cache_fetch_state (int idx, struct bidi_it *bidi_it)
+bidi_cache_fetch_state (EMACS_INT idx, struct bidi_it *bidi_it)
 {
   int current_scan_dir = bidi_it->scan_dir;
 
-  if (idx < 0 || idx >= bidi_cache_idx)
+  if (idx < bidi_cache_start || idx >= bidi_cache_idx)
     abort ();
 
   bidi_copy_it (bidi_it, &bidi_cache[idx]);
@@ -304,13 +352,15 @@
    level less or equal to LEVEL.  if LEVEL is -1, disregard the
    resolved levels in cached states.  DIR, if non-zero, means search
    in that direction from the last cache hit.  */
-static inline int
+static inline EMACS_INT
 bidi_cache_search (EMACS_INT charpos, int level, int dir)
 {
-  int i, i_start;
+  EMACS_INT i, i_start;
 
-  if (bidi_cache_idx)
+  if (bidi_cache_idx > bidi_cache_start)
     {
+      if (bidi_cache_last_idx == -1)
+       bidi_cache_last_idx = bidi_cache_idx - 1;
       if (charpos < bidi_cache[bidi_cache_last_idx].charpos)
        {
          dir = -1;
@@ -333,7 +383,7 @@
       if (dir < 0)
        {
          /* Linear search for now; FIXME!  */
-         for (i = i_start; i >= 0; i--)
+         for (i = i_start; i >= bidi_cache_start; i--)
            if (bidi_cache[i].charpos <= charpos
                && charpos < bidi_cache[i].charpos + bidi_cache[i].nchars
                && (level == -1 || bidi_cache[i].resolved_level <= level))
@@ -355,8 +405,9 @@
 /* Find a cached state where the resolved level changes to a value
    that is lower than LEVEL, and return its cache slot index.  DIR is
    the direction to search, starting with the last used cache slot.
-   BEFORE, if non-zero, means return the index of the slot that is
-   ``before'' the level change in the search direction.  That is,
+   If DIR is zero, we search backwards from the last occupied cache
+   slot.  BEFORE, if non-zero, means return the index of the slot that
+   is ``before'' the level change in the search direction.  That is,
    given the cached levels like this:
 
         1122333442211
@@ -366,14 +417,16 @@
    C, searching backwards (DIR = -1) for LEVEL = 2 will return the
    index of slot B or A, depending whether BEFORE is, respectively,
    non-zero or zero.  */
-static int
+static EMACS_INT
 bidi_cache_find_level_change (int level, int dir, int before)
 {
   if (bidi_cache_idx)
     {
-      int i = dir ? bidi_cache_last_idx : bidi_cache_idx - 1;
+      EMACS_INT i = dir ? bidi_cache_last_idx : bidi_cache_idx - 1;
       int incr = before ? 1 : 0;
 
+      xassert (!dir || bidi_cache_last_idx >= 0);
+
       if (!dir)
        dir = -1;
       else if (!incr)
@@ -381,7 +434,7 @@
 
       if (dir < 0)
        {
-         while (i >= incr)
+         while (i >= bidi_cache_start + incr)
            {
              if (bidi_cache[i - incr].resolved_level >= 0
                  && bidi_cache[i - incr].resolved_level < level)
@@ -405,9 +458,22 @@
 }
 
 static inline void
+bidi_cache_ensure_space (EMACS_INT idx)
+{
+  /* Enlarge the cache as needed.  */
+  if (idx >= bidi_cache_size)
+    {
+      while (idx >= bidi_cache_size)
+       bidi_cache_size += BIDI_CACHE_CHUNK;
+      bidi_cache =
+       (struct bidi_it *) xrealloc (bidi_cache, bidi_cache_size * elsz);
+    }
+}
+
+static inline void
 bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
 {
-  int idx;
+  EMACS_INT idx;
 
   /* We should never cache on backward scans.  */
   if (bidi_it->scan_dir == -1)
@@ -417,23 +483,17 @@
   if (idx < 0)
     {
       idx = bidi_cache_idx;
-      /* Enlarge the cache as needed.  */
-      if (idx >= bidi_cache_size)
-       {
-         bidi_cache_size += BIDI_CACHE_CHUNK;
-         bidi_cache =
-           (struct bidi_it *) xrealloc (bidi_cache, bidi_cache_size * elsz);
-       }
+      bidi_cache_ensure_space (idx);
       /* Character positions should correspond to cache positions 1:1.
         If we are outside the range of cached positions, the cache is
         useless and must be reset.  */
-      if (idx > 0 &&
+      if (idx > bidi_cache_start &&
          (bidi_it->charpos > (bidi_cache[idx - 1].charpos
                               + bidi_cache[idx - 1].nchars)
-          || bidi_it->charpos < bidi_cache[0].charpos))
+          || bidi_it->charpos < bidi_cache[bidi_cache_start].charpos))
        {
          bidi_cache_reset ();
-         idx = 0;
+         idx = bidi_cache_start;
        }
       if (bidi_it->nchars <= 0)
        abort ();
@@ -468,9 +528,9 @@
 static inline bidi_type_t
 bidi_cache_find (EMACS_INT charpos, int level, struct bidi_it *bidi_it)
 {
-  int i = bidi_cache_search (charpos, level, bidi_it->scan_dir);
+  EMACS_INT i = bidi_cache_search (charpos, level, bidi_it->scan_dir);
 
-  if (i >= 0)
+  if (i >= bidi_cache_start)
     {
       bidi_dir_t current_scan_dir = bidi_it->scan_dir;
 
@@ -488,69 +548,245 @@
 static inline int
 bidi_peek_at_next_level (struct bidi_it *bidi_it)
 {
-  if (bidi_cache_idx == 0 || bidi_cache_last_idx == -1)
+  if (bidi_cache_idx == bidi_cache_start || bidi_cache_last_idx == -1)
     abort ();
   return bidi_cache[bidi_cache_last_idx + bidi_it->scan_dir].resolved_level;
 }
 
-/* Check if buffer position CHARPOS/BYTEPOS is the end of a paragraph.
-   Value is the non-negative length of the paragraph separator
-   following the buffer position, -1 if position is at the beginning
-   of a new paragraph, or -2 if position is neither at beginning nor
-   at end of a paragraph.  */
-static EMACS_INT
-bidi_at_paragraph_end (EMACS_INT charpos, EMACS_INT bytepos)
-{
-  Lisp_Object sep_re;
-  Lisp_Object start_re;
-  EMACS_INT val;
-
-  sep_re = paragraph_separate_re;
-  start_re = paragraph_start_re;
-
-  val = fast_looking_at (sep_re, charpos, bytepos, ZV, ZV_BYTE, Qnil);
-  if (val < 0)
-    {
-      if (fast_looking_at (start_re, charpos, bytepos, ZV, ZV_BYTE, Qnil) >= 0)
-       val = -1;
-      else
-       val = -2;
-    }
-
-  return val;
-}
-
-/* Determine the start-of-run (sor) directional type given the two
-   embedding levels on either side of the run boundary.  Also, update
-   the saved info about previously seen characters, since that info is
-   generally valid for a single level run.  */
+
+/***********************************************************************
+            Pushing and popping the bidi iterator state
+ ***********************************************************************/
+/* 5-slot stack for saving the start of the previous level of the
+   cache.  xdisp.c maintains a 5-slot stack for its iterator state,
+   and we need the same size of our stack.  */
+static EMACS_INT bidi_cache_start_stack[IT_STACK_SIZE];
+static int bidi_cache_sp;
+
+/* Push the bidi iterator state in preparation for reordering a
+   different object, e.g. display string found at certain buffer
+   position.  Pushing the bidi iterator boils down to saving its
+   entire state on the cache and starting a new cache "stacked" on top
+   of the current cache.  */
+void
+bidi_push_it (struct bidi_it *bidi_it)
+{
+  /* Save the current iterator state in its entirety after the last
+     used cache slot.  */
+  bidi_cache_ensure_space (bidi_cache_idx);
+  memcpy (&bidi_cache[bidi_cache_idx++], bidi_it, sizeof (struct bidi_it));
+
+  /* Push the current cache start onto the stack.  */
+  xassert (bidi_cache_sp < IT_STACK_SIZE);
+  bidi_cache_start_stack[bidi_cache_sp++] = bidi_cache_start;
+
+  /* Start a new level of cache, and make it empty.  */
+  bidi_cache_start = bidi_cache_idx;
+  bidi_cache_last_idx = -1;
+}
+
+/* Restore the iterator state saved by bidi_push_it and return the
+   cache to the corresponding state.  */
+void
+bidi_pop_it (struct bidi_it *bidi_it)
+{
+  if (bidi_cache_start <= 0)
+    abort ();
+
+  /* Reset the next free cache slot index to what it was before the
+     call to bidi_push_it.  */
+  bidi_cache_idx = bidi_cache_start - 1;
+
+  /* Restore the bidi iterator state saved in the cache.  */
+  memcpy (bidi_it, &bidi_cache[bidi_cache_idx], sizeof (struct bidi_it));
+
+  /* Pop the previous cache start from the stack.  */
+  if (bidi_cache_sp <= 0)
+    abort ();
+  bidi_cache_start = bidi_cache_start_stack[--bidi_cache_sp];
+
+  /* Invalidate the last-used cache slot data.  */
+  bidi_cache_last_idx = -1;
+}
+
+/* Stash away a copy of the cache and its control variables.  */
+void *
+bidi_shelve_cache (void)
+{
+  unsigned char *databuf;
+
+  if (bidi_cache_idx == 0)
+    return NULL;
+
+  databuf = xmalloc (sizeof (bidi_cache_idx)
+                    + bidi_cache_idx * sizeof (struct bidi_it)
+                    + sizeof (bidi_cache_start_stack)
+                    + sizeof (bidi_cache_sp) + sizeof (bidi_cache_start)
+                    + sizeof (bidi_cache_last_idx));
+  memcpy (databuf, &bidi_cache_idx, sizeof (bidi_cache_idx));
+  memcpy (databuf + sizeof (bidi_cache_idx),
+         bidi_cache, bidi_cache_idx * sizeof (struct bidi_it));
+  memcpy (databuf + sizeof (bidi_cache_idx)
+         + bidi_cache_idx * sizeof (struct bidi_it),
+         bidi_cache_start_stack, sizeof (bidi_cache_start_stack));
+  memcpy (databuf + sizeof (bidi_cache_idx)
+         + bidi_cache_idx * sizeof (struct bidi_it)
+         + sizeof (bidi_cache_start_stack),
+         &bidi_cache_sp, sizeof (bidi_cache_sp));
+  memcpy (databuf + sizeof (bidi_cache_idx)
+         + bidi_cache_idx * sizeof (struct bidi_it)
+         + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp),
+         &bidi_cache_start, sizeof (bidi_cache_start));
+  memcpy (databuf + sizeof (bidi_cache_idx)
+         + bidi_cache_idx * sizeof (struct bidi_it)
+         + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp)
+         + sizeof (bidi_cache_start),
+         &bidi_cache_last_idx, sizeof (bidi_cache_last_idx));
+
+  return databuf;
+}
+
+/* Restore the cache state from a copy stashed away by bidi_shelve_cache.  */
+void
+bidi_unshelve_cache (void *databuf)
+{
+  unsigned char *p = databuf;
+
+  if (!p)
+    {
+      /* A NULL pointer means an empty cache.  */
+      bidi_cache_start = 0;
+      bidi_cache_sp = 0;
+      bidi_cache_reset ();
+    }
+  else
+    {
+      memcpy (&bidi_cache_idx, p, sizeof (bidi_cache_idx));
+      bidi_cache_ensure_space (bidi_cache_idx);
+      memcpy (bidi_cache, p + sizeof (bidi_cache_idx),
+             bidi_cache_idx * sizeof (struct bidi_it));
+      memcpy (bidi_cache_start_stack,
+             p + sizeof (bidi_cache_idx)
+             + bidi_cache_idx * sizeof (struct bidi_it),
+             sizeof (bidi_cache_start_stack));
+      memcpy (&bidi_cache_sp,
+             p + sizeof (bidi_cache_idx)
+             + bidi_cache_idx * sizeof (struct bidi_it)
+             + sizeof (bidi_cache_start_stack),
+             sizeof (bidi_cache_sp));
+      memcpy (&bidi_cache_start,
+             p + sizeof (bidi_cache_idx)
+             + bidi_cache_idx * sizeof (struct bidi_it)
+             + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp),
+             sizeof (bidi_cache_start));
+      memcpy (&bidi_cache_last_idx,
+             p + sizeof (bidi_cache_idx)
+             + bidi_cache_idx * sizeof (struct bidi_it)
+             + sizeof (bidi_cache_start_stack) + sizeof (bidi_cache_sp)
+             + sizeof (bidi_cache_start),
+             sizeof (bidi_cache_last_idx));
+
+      xfree (p);
+    }
+}
+
+
+/***********************************************************************
+                       Initialization
+ ***********************************************************************/
+static void
+bidi_initialize (void)
+{
+
+#include "biditype.h"
+#include "bidimirror.h"
+
+  int i;
+
+  bidi_type_table = Fmake_char_table (Qnil, make_number (STRONG_L));
+  staticpro (&bidi_type_table);
+
+  for (i = 0; i < sizeof bidi_type / sizeof bidi_type[0]; i++)
+    char_table_set_range (bidi_type_table, bidi_type[i].from, bidi_type[i].to,
+                         make_number (bidi_type[i].type));
+
+  bidi_mirror_table = Fmake_char_table (Qnil, Qnil);
+  staticpro (&bidi_mirror_table);
+
+  for (i = 0; i < sizeof bidi_mirror / sizeof bidi_mirror[0]; i++)
+    char_table_set (bidi_mirror_table, bidi_mirror[i].from,
+                   make_number (bidi_mirror[i].to));
+
+  Qparagraph_start = intern ("paragraph-start");
+  staticpro (&Qparagraph_start);
+  paragraph_start_re = Fsymbol_value (Qparagraph_start);
+  if (!STRINGP (paragraph_start_re))
+    paragraph_start_re = build_string ("\f\\|[ \t]*$");
+  staticpro (&paragraph_start_re);
+  Qparagraph_separate = intern ("paragraph-separate");
+  staticpro (&Qparagraph_separate);
+  paragraph_separate_re = Fsymbol_value (Qparagraph_separate);
+  if (!STRINGP (paragraph_separate_re))
+    paragraph_separate_re = build_string ("[ \t\f]*$");
+  staticpro (&paragraph_separate_re);
+
+  bidi_cache_sp = 0;
+
+  bidi_initialized = 1;
+}
+
+/* Do whatever UAX#9 clause X8 says should be done at paragraph's
+   end.  */
 static inline void
-bidi_set_sor_type (struct bidi_it *bidi_it, int level_before, int level_after)
-{
-  int higher_level = level_before > level_after ? level_before : level_after;
-
-  /* The prev_was_pdf gork is required for when we have several PDFs
-     in a row.  In that case, we want to compute the sor type for the
-     next level run only once: when we see the first PDF.  That's
-     because the sor type depends only on the higher of the two levels
-     that we find on the two sides of the level boundary (see UAX#9,
-     clause X10), and so we don't need to know the final embedding
-     level to which we descend after processing all the PDFs.  */
-  if (!bidi_it->prev_was_pdf || level_before < level_after)
-    /* FIXME: should the default sor direction be user selectable?  */
-    bidi_it->sor = (higher_level & 1) != 0 ? R2L : L2R;
-  if (level_before > level_after)
-    bidi_it->prev_was_pdf = 1;
-
-  bidi_it->prev.type = UNKNOWN_BT;
+bidi_set_paragraph_end (struct bidi_it *bidi_it)
+{
+  bidi_it->invalid_levels = 0;
+  bidi_it->invalid_rl_levels = -1;
+  bidi_it->stack_idx = 0;
+  bidi_it->resolved_level = bidi_it->level_stack[0].level;
+}
+
+/* Initialize the bidi iterator from buffer/string position CHARPOS.  */
+void
+bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, int frame_window_p,
+             struct bidi_it *bidi_it)
+{
+  if (! bidi_initialized)
+    bidi_initialize ();
+  if (charpos >= 0)
+    bidi_it->charpos = charpos;
+  if (bytepos >= 0)
+    bidi_it->bytepos = bytepos;
+  bidi_it->frame_window_p = frame_window_p;
+  bidi_it->nchars = -1;        /* to be computed in bidi_resolve_explicit_1 */
+  bidi_it->first_elt = 1;
+  bidi_set_paragraph_end (bidi_it);
+  bidi_it->new_paragraph = 1;
+  bidi_it->separator_limit = -1;
+  bidi_it->type = NEUTRAL_B;
+  bidi_it->type_after_w1 = NEUTRAL_B;
+  bidi_it->orig_type = NEUTRAL_B;
+  bidi_it->prev_was_pdf = 0;
+  bidi_it->prev.type = bidi_it->prev.type_after_w1 =
+    bidi_it->prev.orig_type = UNKNOWN_BT;
   bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1 =
     bidi_it->last_strong.orig_type = UNKNOWN_BT;
-  bidi_it->prev_for_neutral.type = bidi_it->sor == R2L ? STRONG_R : STRONG_L;
-  bidi_it->prev_for_neutral.charpos = bidi_it->charpos;
-  bidi_it->prev_for_neutral.bytepos = bidi_it->bytepos;
-  bidi_it->next_for_neutral.type = bidi_it->next_for_neutral.type_after_w1 =
+  bidi_it->next_for_neutral.charpos = -1;
+  bidi_it->next_for_neutral.type =
+    bidi_it->next_for_neutral.type_after_w1 =
     bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
-  bidi_it->ignore_bn_limit = 0; /* meaning it's unknown */
+  bidi_it->prev_for_neutral.charpos = -1;
+  bidi_it->prev_for_neutral.type =
+    bidi_it->prev_for_neutral.type_after_w1 =
+    bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
+  bidi_it->sor = L2R;   /* FIXME: should it be user-selectable? */
+  bidi_it->disp_pos = -1;      /* invalid/unknown */
+  /* We can only shrink the cache if we are at the bottom level of its
+     "stack".  */
+  if (bidi_cache_start == 0)
+    bidi_cache_shrink ();
+  else
+    bidi_cache_reset ();
 }
 
 /* Perform initializations for reordering a new line of bidi text.  */
@@ -571,6 +807,57 @@
   bidi_cache_reset ();
 }
 
+
+/***********************************************************************
+                       Fetching characters
+ ***********************************************************************/
+
+/* Count bytes in string S between BEG/BEGBYTE and END.  BEG and END
+   are zero-based character positions in S, BEGBYTE is byte position
+   corresponding to BEG.  UNIBYTE, if non-zero, means S is a unibyte
+   string.  */
+static inline EMACS_INT
+bidi_count_bytes (const unsigned char *s, const EMACS_INT beg,
+                 const EMACS_INT begbyte, const EMACS_INT end, int unibyte)
+{
+  EMACS_INT pos = beg;
+  const unsigned char *p = s + begbyte, *start = p;
+
+  if (unibyte)
+    p = s + end;
+  else
+    {
+      if (!CHAR_HEAD_P (*p))
+       abort ();
+
+      while (pos < end)
+       {
+         p += BYTES_BY_CHAR_HEAD (*p);
+         pos++;
+       }
+    }
+
+  return p - start;
+}
+
+/* Fetch and returns the character at byte position BYTEPOS.  If S is
+   non-NULL, fetch the character from string S; otherwise fetch the
+   character from the current buffer.  UNIBYTE non-zero means S is a
+   unibyte string.  */
+static inline int
+bidi_char_at_pos (EMACS_INT bytepos, const unsigned char *s, int unibyte)
+{
+  if (s)
+    {
+      if (unibyte)
+       return s[bytepos];
+      else
+       return STRING_CHAR (s + bytepos);
+    }
+  else
+    return FETCH_MULTIBYTE_CHAR (bytepos);
+}
+
 /* Fetch and return the character at BYTEPOS/CHARPOS.  If that
    character is covered by a display string, treat the entire run of
    covered characters as a single character u+FFFC, and return their
@@ -578,26 +865,34 @@
    character position of the next display string, or -1 if not yet
    computed.  When the next character is at or beyond that position,
    the function updates DISP_POS with the position of the next display
-   string.  */
+   string.  STRING->s is the C string to iterate, or NULL if iterating
+   over a buffer or a Lisp string; in the latter case, STRING->lstring
+   is the Lisp string.  */
 static inline int
 bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
+                struct bidi_string_data *string,
                 int frame_window_p, EMACS_INT *ch_len, EMACS_INT *nchars)
 {
   int ch;
+  EMACS_INT endpos =
+    (string->s || STRINGP (string->lstring)) ? string->schars : ZV;
+  struct text_pos pos;
 
-  /* FIXME: Support strings in addition to buffers.  */
   /* If we got past the last known position of display string, compute
-     the position of the next one.  That position could be at BYTEPOS.  */
-  if (charpos < ZV && charpos > *disp_pos)
-    *disp_pos = compute_display_string_pos (charpos, frame_window_p);
+     the position of the next one.  That position could be at CHARPOS.  */
+  if (charpos < endpos && charpos > *disp_pos)
+    {
+      SET_TEXT_POS (pos, charpos, bytepos);
+      *disp_pos = compute_display_string_pos (&pos, string, frame_window_p);
+    }
 
   /* Fetch the character at BYTEPOS.  */
-  if (bytepos >= ZV_BYTE)
+  if (charpos >= endpos)
     {
       ch = BIDI_EOB;
       *ch_len = 1;
       *nchars = 1;
-      *disp_pos = ZV;
+      *disp_pos = endpos;
     }
   else if (charpos >= *disp_pos)
     {
@@ -608,28 +903,105 @@
       if (charpos > *disp_pos)
        abort ();
       /* Return the Unicode Object Replacement Character to represent
-        the entire run of characters covered by the display
-        string.  */
+        the entire run of characters covered by the display string.  */
       ch = 0xFFFC;
-      disp_end_pos = compute_display_string_end (*disp_pos);
+      disp_end_pos = compute_display_string_end (*disp_pos, string);
       *nchars = disp_end_pos - *disp_pos;
-      *ch_len = CHAR_TO_BYTE (disp_end_pos) - bytepos;
+      if (*nchars <= 0)
+       abort ();
+      if (string->s)
+       *ch_len = bidi_count_bytes (string->s, *disp_pos, bytepos,
+                                   disp_end_pos, string->unibyte);
+      else if (STRINGP (string->lstring))
+       *ch_len = bidi_count_bytes (SDATA (string->lstring), *disp_pos,
+                                   bytepos, disp_end_pos, string->unibyte);
+      else
+       *ch_len = CHAR_TO_BYTE (disp_end_pos) - bytepos;
     }
   else
     {
-      ch = FETCH_MULTIBYTE_CHAR (bytepos);
+      if (string->s)
+       {
+         int len;
+
+         if (!string->unibyte)
+           {
+             ch = STRING_CHAR_AND_LENGTH (string->s + bytepos, len);
+             *ch_len = len;
+           }
+         else
+           {
+             ch = UNIBYTE_TO_CHAR (string->s[bytepos]);
+             *ch_len = 1;
+           }
+       }
+      else if (STRINGP (string->lstring))
+       {
+         int len;
+
+         if (!string->unibyte)
+           {
+             ch = STRING_CHAR_AND_LENGTH (SDATA (string->lstring) + bytepos,
+                                          len);
+             *ch_len = len;
+           }
+         else
+           {
+             ch = UNIBYTE_TO_CHAR (SREF (string->lstring, bytepos));
+             *ch_len = 1;
+           }
+       }
+      else
+       {
+         ch = FETCH_MULTIBYTE_CHAR (bytepos);
+         *ch_len = CHAR_BYTES (ch);
+       }
       *nchars = 1;
-      *ch_len = CHAR_BYTES (ch);
     }
 
   /* If we just entered a run of characters covered by a display
      string, compute the position of the next display string.  */
-  if (charpos + *nchars <= ZV && charpos + *nchars > *disp_pos)
-    *disp_pos = compute_display_string_pos (charpos + *nchars, frame_window_p);
+  if (charpos + *nchars <= endpos && charpos + *nchars > *disp_pos)
+    {
+      SET_TEXT_POS (pos, charpos + *nchars, bytepos + *ch_len);
+      *disp_pos = compute_display_string_pos (&pos, string, frame_window_p);
+    }
 
   return ch;
 }
 
+
+/***********************************************************************
+                       Determining paragraph direction
+ ***********************************************************************/
+
+/* Check if buffer position CHARPOS/BYTEPOS is the end of a paragraph.
+   Value is the non-negative length of the paragraph separator
+   following the buffer position, -1 if position is at the beginning
+   of a new paragraph, or -2 if position is neither at beginning nor
+   at end of a paragraph.  */
+static EMACS_INT
+bidi_at_paragraph_end (EMACS_INT charpos, EMACS_INT bytepos)
+{
+  Lisp_Object sep_re;
+  Lisp_Object start_re;
+  EMACS_INT val;
+
+  sep_re = paragraph_separate_re;
+  start_re = paragraph_start_re;
+
+  val = fast_looking_at (sep_re, charpos, bytepos, ZV, ZV_BYTE, Qnil);
+  if (val < 0)
+    {
+      if (fast_looking_at (start_re, charpos, bytepos, ZV, ZV_BYTE, Qnil) >= 0)
+       val = -1;
+      else
+       val = -2;
+    }
+
+  return val;
+}
+
 /* Find the beginning of this paragraph by looking back in the buffer.
    Value is the byte position of the paragraph's beginning.  */
 static EMACS_INT
@@ -670,13 +1042,19 @@
 bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
 {
   EMACS_INT bytepos = bidi_it->bytepos;
+  int string_p = bidi_it->string.s != NULL || STRINGP 
(bidi_it->string.lstring);
   EMACS_INT pstartbyte;
+  /* Note that begbyte is a byte position, while end is a character
+     position.  Yes, this is ugly, but we are trying to avoid costly
+     calls to BYTE_TO_CHAR and its ilk.  */
+  EMACS_INT begbyte = string_p ? 0 : BEGV_BYTE;
+  EMACS_INT end = string_p ? bidi_it->string.schars : ZV;
 
   /* Special case for an empty buffer. */
-  if (bytepos == BEGV_BYTE && bytepos == ZV_BYTE)
+  if (bytepos == begbyte && bidi_it->charpos == end)
     dir = L2R;
   /* We should never be called at EOB or before BEGV.  */
-  else if (bytepos >= ZV_BYTE || bytepos < BEGV_BYTE)
+  else if (bidi_it->charpos >= end || bytepos < begbyte)
     abort ();
 
   if (dir == L2R)
@@ -695,6 +1073,7 @@
       EMACS_INT ch_len, nchars;
       EMACS_INT pos, disp_pos = -1;
       bidi_type_t type;
+      const unsigned char *s;
 
       if (!bidi_initialized)
        bidi_initialize ();
@@ -712,7 +1091,10 @@
         we are potentially in a new paragraph that doesn't yet
         exist.  */
       pos = bidi_it->charpos;
-      if (bytepos > BEGV_BYTE && FETCH_CHAR (bytepos) == '\n')
+      s = STRINGP (bidi_it->string.lstring) ?
+       SDATA (bidi_it->string.lstring) : bidi_it->string.s;
+      if (bytepos > begbyte
+         && bidi_char_at_pos (bytepos, s, bidi_it->string.unibyte) == '\n')
        {
          bytepos++;
          pos++;
@@ -720,17 +1102,25 @@
 
       /* We are either at the beginning of a paragraph or in the
         middle of it.  Find where this paragraph starts.  */
-      pstartbyte = bidi_find_paragraph_start (pos, bytepos);
+      if (string_p)
+       {
+         /* We don't support changes of paragraph direction inside a
+            string.  It is treated as a single paragraph.  */
+         pstartbyte = 0;
+       }
+      else
+       pstartbyte = bidi_find_paragraph_start (pos, bytepos);
       bidi_it->separator_limit = -1;
       bidi_it->new_paragraph = 0;
 
       /* The following loop is run more than once only if NO_DEFAULT_P
-        is non-zero.  */
+        is non-zero, and only if we are iterating on a buffer.  */
       do {
        bytepos = pstartbyte;
-       pos = BYTE_TO_CHAR (bytepos);
-       ch = bidi_fetch_char (bytepos, pos, &disp_pos, bidi_it->frame_window_p,
-                             &ch_len, &nchars);
+       if (!string_p)
+         pos = BYTE_TO_CHAR (bytepos);
+       ch = bidi_fetch_char (bytepos, pos, &disp_pos, &bidi_it->string,
+                             bidi_it->frame_window_p, &ch_len, &nchars);
        type = bidi_get_type (ch, NEUTRAL_DIR);
 
        for (pos += nchars, bytepos += ch_len;
@@ -744,17 +1134,19 @@
                       || type == LRE || type == LRO));
             type = bidi_get_type (ch, NEUTRAL_DIR))
          {
-           if (bytepos >= ZV_BYTE)
+           if (pos >= end)
              {
                /* Pretend there's a paragraph separator at end of
-                  buffer.  */
+                  buffer/string.  */
                type = NEUTRAL_B;
                break;
              }
-           if (type == NEUTRAL_B && bidi_at_paragraph_end (pos, bytepos) >= -1)
+           if (!string_p
+               && type == NEUTRAL_B
+               && bidi_at_paragraph_end (pos, bytepos) >= -1)
              break;
            /* Fetch next character and advance to get past it.  */
-           ch = bidi_fetch_char (bytepos, pos, &disp_pos,
+           ch = bidi_fetch_char (bytepos, pos, &disp_pos, &bidi_it->string,
                                  bidi_it->frame_window_p, &ch_len, &nchars);
            pos += nchars;
            bytepos += ch_len;
@@ -763,7 +1155,8 @@
          bidi_it->paragraph_dir = R2L;
        else if (type == STRONG_L)
          bidi_it->paragraph_dir = L2R;
-       if (no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR)
+       if (!string_p
+           && no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR)
          {
            /* If this paragraph is at BEGV, default to L2R.  */
            if (pstartbyte == BEGV_BYTE)
@@ -786,7 +1179,8 @@
                pstartbyte = prevpbyte;
              }
          }
-      } while (no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR);
+      } while (!string_p
+              && no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR);
     }
   else
     abort ();
@@ -804,110 +1198,11 @@
   bidi_line_init (bidi_it);
 }
 
-/* Do whatever UAX#9 clause X8 says should be done at paragraph's
-   end.  */
-static inline void
-bidi_set_paragraph_end (struct bidi_it *bidi_it)
-{
-  bidi_it->invalid_levels = 0;
-  bidi_it->invalid_rl_levels = -1;
-  bidi_it->stack_idx = 0;
-  bidi_it->resolved_level = bidi_it->level_stack[0].level;
-}
-
-/* Initialize the bidi iterator from buffer/string position CHARPOS.  */
-void
-bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, int frame_window_p,
-             struct bidi_it *bidi_it)
-{
-  if (! bidi_initialized)
-    bidi_initialize ();
-  bidi_it->charpos = charpos;
-  bidi_it->bytepos = bytepos;
-  bidi_it->frame_window_p = frame_window_p;
-  bidi_it->nchars = -1;        /* to be computed in bidi_resolve_explicit_1 */
-  bidi_it->first_elt = 1;
-  bidi_set_paragraph_end (bidi_it);
-  bidi_it->new_paragraph = 1;
-  bidi_it->separator_limit = -1;
-  bidi_it->type = NEUTRAL_B;
-  bidi_it->type_after_w1 = NEUTRAL_B;
-  bidi_it->orig_type = NEUTRAL_B;
-  bidi_it->prev_was_pdf = 0;
-  bidi_it->prev.type = bidi_it->prev.type_after_w1 =
-    bidi_it->prev.orig_type = UNKNOWN_BT;
-  bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1 =
-    bidi_it->last_strong.orig_type = UNKNOWN_BT;
-  bidi_it->next_for_neutral.charpos = -1;
-  bidi_it->next_for_neutral.type =
-    bidi_it->next_for_neutral.type_after_w1 =
-    bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
-  bidi_it->prev_for_neutral.charpos = -1;
-  bidi_it->prev_for_neutral.type =
-    bidi_it->prev_for_neutral.type_after_w1 =
-    bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
-  bidi_it->sor = L2R;   /* FIXME: should it be user-selectable? */
-  bidi_it->disp_pos = -1;      /* invalid/unknown */
-  bidi_cache_shrink ();
-}
-
-/* Push the current embedding level and override status; reset the
-   current level to LEVEL and the current override status to OVERRIDE.  */
-static inline void
-bidi_push_embedding_level (struct bidi_it *bidi_it,
-                          int level, bidi_dir_t override)
-{
-  bidi_it->stack_idx++;
-  if (bidi_it->stack_idx >= BIDI_MAXLEVEL)
-    abort ();
-  bidi_it->level_stack[bidi_it->stack_idx].level = level;
-  bidi_it->level_stack[bidi_it->stack_idx].override = override;
-}
-
-/* Pop the embedding level and directional override status from the
-   stack, and return the new level.  */
-static inline int
-bidi_pop_embedding_level (struct bidi_it *bidi_it)
-{
-  /* UAX#9 says to ignore invalid PDFs.  */
-  if (bidi_it->stack_idx > 0)
-    bidi_it->stack_idx--;
-  return bidi_it->level_stack[bidi_it->stack_idx].level;
-}
-
-/* Record in SAVED_INFO the information about the current character.  */
-static inline void
-bidi_remember_char (struct bidi_saved_info *saved_info,
-                   struct bidi_it *bidi_it)
-{
-  saved_info->charpos = bidi_it->charpos;
-  saved_info->bytepos = bidi_it->bytepos;
-  saved_info->type = bidi_it->type;
-  bidi_check_type (bidi_it->type);
-  saved_info->type_after_w1 = bidi_it->type_after_w1;
-  bidi_check_type (bidi_it->type_after_w1);
-  saved_info->orig_type = bidi_it->orig_type;
-  bidi_check_type (bidi_it->orig_type);
-}
-
-/* Resolve the type of a neutral character according to the type of
-   surrounding strong text and the current embedding level.  */
-static inline bidi_type_t
-bidi_resolve_neutral_1 (bidi_type_t prev_type, bidi_type_t next_type, int lev)
-{
-  /* N1: European and Arabic numbers are treated as though they were R.  */
-  if (next_type == WEAK_EN || next_type == WEAK_AN)
-    next_type = STRONG_R;
-  if (prev_type == WEAK_EN || prev_type == WEAK_AN)
-    prev_type = STRONG_R;
-
-  if (next_type == prev_type)  /* N1 */
-    return next_type;
-  else if ((lev & 1) == 0)     /* N2 */
-    return STRONG_L;
-  else
-    return STRONG_R;
-}
+
+/***********************************************************************
+                Resolving explicit and implicit levels.
+  The rest of this file constitutes the core of the UBA implementation.
+ ***********************************************************************/
 
 static inline int
 bidi_explicit_dir_char (int ch)
@@ -934,19 +1229,35 @@
   int current_level;
   int new_level;
   bidi_dir_t override;
+  int string_p = bidi_it->string.s != NULL || STRINGP 
(bidi_it->string.lstring);
 
   /* If reseat()'ed, don't advance, so as to start iteration from the
      position where we were reseated.  bidi_it->bytepos can be less
      than BEGV_BYTE after reseat to BEGV.  */
-  if (bidi_it->bytepos < BEGV_BYTE
+  if (bidi_it->bytepos < (string_p ? 0 : BEGV_BYTE)
       || bidi_it->first_elt)
     {
       bidi_it->first_elt = 0;
-      if (bidi_it->charpos < BEGV)
-       bidi_it->charpos = BEGV;
-      bidi_it->bytepos = CHAR_TO_BYTE (bidi_it->charpos);
+      if (string_p)
+       {
+         const unsigned char *p =
+           STRINGP (bidi_it->string.lstring)
+           ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
+
+         if (bidi_it->charpos < 0)
+           bidi_it->charpos = 0;
+         bidi_it->bytepos = bidi_count_bytes (p, 0, 0, bidi_it->charpos,
+                                              bidi_it->string.unibyte);
+       }
+      else
+       {
+         if (bidi_it->charpos < BEGV)
+           bidi_it->charpos = BEGV;
+         bidi_it->bytepos = CHAR_TO_BYTE (bidi_it->charpos);
+       }
     }
-  else if (bidi_it->bytepos < ZV_BYTE) /* don't move at ZV */
+  /* Don't move at end of buffer/string.  */
+  else if (bidi_it->charpos < (string_p ? bidi_it->string.schars : ZV))
     {
       /* Advance to the next character, skipping characters covered by
         display strings (nchars > 1).  */
@@ -962,12 +1273,12 @@
   override = bidi_it->level_stack[bidi_it->stack_idx].override;
   new_level = current_level;
 
-  if (bidi_it->bytepos >= ZV_BYTE)
+  if (bidi_it->charpos >= (string_p ? bidi_it->string.schars : ZV))
     {
       curchar = BIDI_EOB;
       bidi_it->ch_len = 1;
       bidi_it->nchars = 1;
-      bidi_it->disp_pos = ZV;
+      bidi_it->disp_pos = (string_p ? bidi_it->string.schars : ZV);
     }
   else
     {
@@ -975,7 +1286,8 @@
         display string, treat the entire run of covered characters as
         a single character u+FFFC.  */
       curchar = bidi_fetch_char (bidi_it->bytepos, bidi_it->charpos,
-                                &bidi_it->disp_pos, bidi_it->frame_window_p,
+                                &bidi_it->disp_pos, &bidi_it->string,
+                                bidi_it->frame_window_p,
                                 &bidi_it->ch_len, &bidi_it->nchars);
     }
   bidi_it->ch = curchar;
@@ -1000,7 +1312,7 @@
        bidi_it->type_after_w1 = type;
        bidi_check_type (bidi_it->type_after_w1);
        type = WEAK_BN; /* X9/Retaining */
-       if (bidi_it->ignore_bn_limit <= 0)
+       if (bidi_it->ignore_bn_limit <= -1)
          {
            if (current_level <= BIDI_MAXLEVEL - 4)
              {
@@ -1033,7 +1345,7 @@
        bidi_it->type_after_w1 = type;
        bidi_check_type (bidi_it->type_after_w1);
        type = WEAK_BN; /* X9/Retaining */
-       if (bidi_it->ignore_bn_limit <= 0)
+       if (bidi_it->ignore_bn_limit <= -1)
          {
            if (current_level <= BIDI_MAXLEVEL - 5)
              {
@@ -1068,7 +1380,7 @@
        bidi_it->type_after_w1 = type;
        bidi_check_type (bidi_it->type_after_w1);
        type = WEAK_BN; /* X9/Retaining */
-       if (bidi_it->ignore_bn_limit <= 0)
+       if (bidi_it->ignore_bn_limit <= -1)
          {
            if (!bidi_it->invalid_rl_levels)
              {
@@ -1111,13 +1423,17 @@
 {
   int prev_level = bidi_it->level_stack[bidi_it->stack_idx].level;
   int new_level  = bidi_resolve_explicit_1 (bidi_it);
+  EMACS_INT eob = bidi_it->string.s ? bidi_it->string.schars : ZV;
+  const unsigned char *s = STRINGP (bidi_it->string.lstring)
+    ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
 
   if (prev_level < new_level
       && bidi_it->type == WEAK_BN
-      && bidi_it->ignore_bn_limit == 0 /* only if not already known */
-      && bidi_it->bytepos < ZV_BYTE    /* not already at EOB */
-      && bidi_explicit_dir_char (FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
-                                                      + bidi_it->ch_len)))
+      && bidi_it->ignore_bn_limit == -1 /* only if not already known */
+      && bidi_it->charpos < eob                /* not already at EOB */
+      && bidi_explicit_dir_char (bidi_char_at_pos (bidi_it->bytepos
+                                                  + bidi_it->ch_len, s,
+                                                  bidi_it->string.unibyte)))
     {
       /* Avoid pushing and popping embedding levels if the level run
         is empty, as this breaks level runs where it shouldn't.
@@ -1129,12 +1445,17 @@
 
       bidi_copy_it (&saved_it, bidi_it);
 
-      while (bidi_explicit_dir_char (FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
-                                                          + bidi_it->ch_len)))
+      while (bidi_explicit_dir_char (bidi_char_at_pos (bidi_it->bytepos
+                                                      + bidi_it->ch_len, s,
+                                                      
bidi_it->string.unibyte)))
        {
          /* This advances to the next character, skipping any
             characters covered by display strings.  */
          level = bidi_resolve_explicit_1 (bidi_it);
+         /* If string.lstring was relocated inside bidi_resolve_explicit_1,
+            a pointer to its data is no longer valid.  */
+         if (STRINGP (bidi_it->string.lstring))
+           s = SDATA (bidi_it->string.lstring);
        }
 
       if (bidi_it->nchars <= 0)
@@ -1142,10 +1463,10 @@
       if (level == prev_level) /* empty embedding */
        saved_it.ignore_bn_limit = bidi_it->charpos + bidi_it->nchars;
       else                     /* this embedding is non-empty */
-       saved_it.ignore_bn_limit = -1;
+       saved_it.ignore_bn_limit = -2;
 
       bidi_copy_it (bidi_it, &saved_it);
-      if (bidi_it->ignore_bn_limit > 0)
+      if (bidi_it->ignore_bn_limit > -1)
        {
          /* We pushed a level, but we shouldn't have.  Undo that. */
          if (!bidi_it->invalid_rl_levels)
@@ -1188,6 +1509,9 @@
   int next_char;
   bidi_type_t type_of_next;
   struct bidi_it saved_it;
+  EMACS_INT eob =
+    (STRINGP (bidi_it->string.lstring) || bidi_it->string.s)
+    ? bidi_it->string.schars : ZV;
 
   type = bidi_it->type;
   override = bidi_it->level_stack[bidi_it->stack_idx].override;
@@ -1254,10 +1578,15 @@
                        && bidi_it->prev.orig_type == WEAK_EN)
                       || bidi_it->prev.type_after_w1 == WEAK_AN)))
        {
+         const unsigned char *s =
+           STRINGP (bidi_it->string.lstring)
+           ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
+
          next_char =
-           bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
-           ? BIDI_EOB : FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
-                                              + bidi_it->ch_len);
+           bidi_it->charpos + bidi_it->nchars >= eob
+           ? BIDI_EOB
+           : bidi_char_at_pos (bidi_it->bytepos + bidi_it->ch_len, s,
+                               bidi_it->string.unibyte);
          type_of_next = bidi_get_type (next_char, override);
 
          if (type_of_next == WEAK_BN
@@ -1306,13 +1635,17 @@
          else                  /* W5: ET/BN with EN after it.  */
            {
              EMACS_INT en_pos = bidi_it->charpos + bidi_it->nchars;
+             const unsigned char *s =
+               STRINGP (bidi_it->string.lstring)
+               ? SDATA (bidi_it->string.lstring) : bidi_it->string.s;
 
              if (bidi_it->nchars <= 0)
                abort ();
              next_char =
-               bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
-               ? BIDI_EOB : FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
-                                                  + bidi_it->ch_len);
+               bidi_it->charpos + bidi_it->nchars >= eob
+               ? BIDI_EOB
+               : bidi_char_at_pos (bidi_it->bytepos + bidi_it->ch_len, s,
+                                   bidi_it->string.unibyte);
              type_of_next = bidi_get_type (next_char, override);
 
              if (type_of_next == WEAK_ET
@@ -1373,6 +1706,25 @@
   return type;
 }
 
+/* Resolve the type of a neutral character according to the type of
+   surrounding strong text and the current embedding level.  */
+static inline bidi_type_t
+bidi_resolve_neutral_1 (bidi_type_t prev_type, bidi_type_t next_type, int lev)
+{
+  /* N1: European and Arabic numbers are treated as though they were R.  */
+  if (next_type == WEAK_EN || next_type == WEAK_AN)
+    next_type = STRONG_R;
+  if (prev_type == WEAK_EN || prev_type == WEAK_AN)
+    prev_type = STRONG_R;
+
+  if (next_type == prev_type)  /* N1 */
+    return next_type;
+  else if ((lev & 1) == 0)     /* N2 */
+    return STRONG_L;
+  else
+    return STRONG_R;
+}
+
 static bidi_type_t
 bidi_resolve_neutral (struct bidi_it *bidi_it)
 {
@@ -1509,11 +1861,11 @@
 
   /* Reset the limit until which to ignore BNs if we step out of the
      area where we found only empty levels.  */
-  if ((bidi_it->ignore_bn_limit > 0
+  if ((bidi_it->ignore_bn_limit > -1
        && bidi_it->ignore_bn_limit <= bidi_it->charpos)
-      || (bidi_it->ignore_bn_limit == -1
+      || (bidi_it->ignore_bn_limit == -2
          && !bidi_explicit_dir_char (bidi_it->ch)))
-    bidi_it->ignore_bn_limit = 0;
+    bidi_it->ignore_bn_limit = -1;
 
   type = bidi_resolve_neutral (bidi_it);
 
@@ -1530,12 +1882,16 @@
   bidi_type_t type;
   int level, prev_level = -1;
   struct bidi_saved_info next_for_neutral;
-  EMACS_INT next_char_pos;
+  EMACS_INT next_char_pos = -2;
 
   if (bidi_it->scan_dir == 1)
     {
+      EMACS_INT eob =
+       (bidi_it->string.s || STRINGP (bidi_it->string.lstring))
+       ? bidi_it->string.schars : ZV;
+
       /* There's no sense in trying to advance if we hit end of text.  */
-      if (bidi_it->bytepos >= ZV_BYTE)
+      if (bidi_it->charpos >= eob)
        return bidi_it->resolved_level;
 
       /* Record the info about the previous character.  */
@@ -1575,17 +1931,27 @@
   /* Perhaps the character we want is already cached.  If it is, the
      call to bidi_cache_find below will return a type other than
      UNKNOWN_BT.  */
-  if (bidi_cache_idx && !bidi_it->first_elt)
+  if (bidi_cache_idx > bidi_cache_start && !bidi_it->first_elt)
     {
+      int bob =
+       (bidi_it->string.s || STRINGP (bidi_it->string.lstring)) ? 0 : 1;
+
       if (bidi_it->scan_dir > 0)
        {
          if (bidi_it->nchars <= 0)
            abort ();
          next_char_pos = bidi_it->charpos + bidi_it->nchars;
        }
-      else
+      else if (bidi_it->charpos >= bob)
+       /* Implementation note: we allow next_char_pos to be as low as
+          0 for buffers or -1 for strings, and that is okay because
+          that's the "position" of the sentinel iterator state we
+          cached at the beginning of the iteration.  */
        next_char_pos = bidi_it->charpos - 1;
-      type = bidi_cache_find (next_char_pos, -1, bidi_it);
+      if (next_char_pos >= bob - 1)
+       type = bidi_cache_find (next_char_pos, -1, bidi_it);
+      else
+       type = UNKNOWN_BT;
     }
   else
     type = UNKNOWN_BT;
@@ -1652,13 +2018,14 @@
       EMACS_INT cpos = bidi_it->charpos;
       EMACS_INT disp_pos = bidi_it->disp_pos;
       EMACS_INT nc = bidi_it->nchars;
+      struct bidi_string_data bs = bidi_it->string;
       bidi_type_t chtype;
       int fwp = bidi_it->frame_window_p;
 
       if (bidi_it->nchars <= 0)
        abort ();
       do {
-       ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, fwp,
+       ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, &bs, fwp,
                              &clen, &nc);
        if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */)
          chtype = NEUTRAL_B;
@@ -1756,10 +2123,11 @@
 bidi_find_other_level_edge (struct bidi_it *bidi_it, int level, int end_flag)
 {
   int dir = end_flag ? -bidi_it->scan_dir : bidi_it->scan_dir;
-  int idx;
+  EMACS_INT idx;
 
   /* Try the cache first.  */
-  if ((idx = bidi_cache_find_level_change (level, dir, end_flag)) >= 0)
+  if ((idx = bidi_cache_find_level_change (level, dir, end_flag))
+      >= bidi_cache_start)
     bidi_cache_fetch_state (idx, bidi_it);
   else
     {
@@ -1781,12 +2149,21 @@
 {
   int old_level, new_level, next_level;
   struct bidi_it sentinel;
+  struct gcpro gcpro1;
+
+  if (bidi_it->charpos < 0 || bidi_it->bytepos < 0)
+    abort ();
 
   if (bidi_it->scan_dir == 0)
     {
       bidi_it->scan_dir = 1;   /* default to logical order */
     }
 
+  /* The code below can call eval, and thus cause GC.  If we are
+     iterating a Lisp string, make sure it won't be GCed.  */
+  if (STRINGP (bidi_it->string.lstring))
+    GCPRO1 (bidi_it->string.lstring);
+
   /* If we just passed a newline, initialize for the next line.  */
   if (!bidi_it->first_elt && bidi_it->orig_type == NEUTRAL_B)
     bidi_line_init (bidi_it);
@@ -1794,7 +2171,7 @@
   /* Prepare the sentinel iterator state, and cache it.  When we bump
      into it, scanning backwards, we'll know that the last non-base
      level is exhausted.  */
-  if (bidi_cache_idx == 0)
+  if (bidi_cache_idx == bidi_cache_start)
     {
       bidi_copy_it (&sentinel, bidi_it);
       if (bidi_it->first_elt)
@@ -1869,26 +2246,34 @@
      reordering, whereas we _must_ know the paragraph base direction
      _before_ we process the paragraph's text, since the base
      direction affects the reordering.  */
-  if (bidi_it->scan_dir == 1
-      && bidi_it->orig_type == NEUTRAL_B
-      && bidi_it->bytepos < ZV_BYTE)
+  if (bidi_it->scan_dir == 1 && bidi_it->orig_type == NEUTRAL_B)
     {
-      EMACS_INT sep_len =
-       bidi_at_paragraph_end (bidi_it->charpos + bidi_it->nchars,
-                              bidi_it->bytepos + bidi_it->ch_len);
-      if (bidi_it->nchars <= 0)
-       abort ();
-      if (sep_len >= 0)
+      /* The paragraph direction of the entire string, once
+        determined, is in effect for the entire string.  Setting the
+        separator limit to the end of the string prevents
+        bidi_paragraph_init from being called automatically on this
+        string.  */
+      if (bidi_it->string.s || STRINGP (bidi_it->string.lstring))
+       bidi_it->separator_limit = bidi_it->string.schars;
+      else if (bidi_it->bytepos < ZV_BYTE)
        {
-         bidi_it->new_paragraph = 1;
-         /* Record the buffer position of the last character of the
-            paragraph separator.  */
-         bidi_it->separator_limit =
-           bidi_it->charpos + bidi_it->nchars + sep_len;
+         EMACS_INT sep_len =
+           bidi_at_paragraph_end (bidi_it->charpos + bidi_it->nchars,
+                                  bidi_it->bytepos + bidi_it->ch_len);
+         if (bidi_it->nchars <= 0)
+           abort ();
+         if (sep_len >= 0)
+           {
+             bidi_it->new_paragraph = 1;
+             /* Record the buffer position of the last character of the
+                paragraph separator.  */
+             bidi_it->separator_limit =
+               bidi_it->charpos + bidi_it->nchars + sep_len;
+           }
        }
     }
 
-  if (bidi_it->scan_dir == 1 && bidi_cache_idx)
+  if (bidi_it->scan_dir == 1 && bidi_cache_idx > bidi_cache_start)
     {
       /* If we are at paragraph's base embedding level and beyond the
         last cached position, the cache's job is done and we can
@@ -1904,6 +2289,9 @@
       else
        bidi_cache_iterator_state (bidi_it, 1);
     }
+
+  if (STRINGP (bidi_it->string.lstring))
+    UNGCPRO;
 }
 
 /* This is meant to be called from within the debugger, whenever you

=== modified file 'src/dispextern.h'
--- a/src/dispextern.h  2011-07-06 22:43:48 +0000
+++ b/src/dispextern.h  2011-07-14 17:28:42 +0000
@@ -1820,9 +1820,21 @@
   bidi_dir_t override;
 };
 
+/* Data type for storing information about a string being iterated on.  */
+struct bidi_string_data {
+  Lisp_Object lstring;         /* Lisp string to reorder, or nil */
+  const unsigned char *s;      /* string data, or NULL if reordering buffer */
+  EMACS_INT schars;            /* the number of characters in the string,
+                                  excluding the terminating null */
+  EMACS_INT bufpos;            /* buffer position of lstring, or 0 if N/A */
+  unsigned from_disp_str : 1;  /* 1 means the string comes from a
+                                  display property */
+  unsigned unibyte : 1;                /* 1 means the string is unibyte */
+};
+
 /* Data type for reordering bidirectional text.  */
 struct bidi_it {
-  EMACS_INT bytepos;           /* iterator's position in buffer */
+  EMACS_INT bytepos;           /* iterator's position in buffer/string */
   EMACS_INT charpos;
   int ch;                      /* character at that position, or u+FFFC
                                   ("object replacement character") for a run
@@ -1852,12 +1864,13 @@
      iterator state is saved, pushed, or popped.  So only put here
      stuff that is not part of the bidi iterator's state!  */
   struct bidi_stack level_stack[BIDI_MAXLEVEL]; /* stack of embedding levels */
-  int first_elt;               /* if non-zero, examine current char first */
+  struct bidi_string_data string;      /* string to reorder */
   bidi_dir_t paragraph_dir;    /* current paragraph direction */
-  int new_paragraph;           /* if non-zero, we expect a new paragraph */
-  int frame_window_p;          /* non-zero if displaying on a GUI frame */
   EMACS_INT separator_limit;   /* where paragraph separator should end */
   EMACS_INT disp_pos;          /* position of display string after ch */
+  unsigned first_elt : 1;      /* if non-zero, examine current char first */
+  unsigned new_paragraph : 1;  /* if non-zero, we expect a new paragraph */
+  unsigned frame_window_p : 1; /* non-zero if displaying on a GUI frame */
 };
 
 /* Value is non-zero when the bidi iterator is at base paragraph
@@ -2134,6 +2147,10 @@
      Don't handle some `display' properties in these strings.  */
   unsigned string_from_display_prop_p : 1;
 
+  /* 1 means we are iterating an object that came from a value of a
+     `display' property.  */
+  unsigned from_disp_prop_p : 1;
+
   /* When METHOD == next_element_from_display_vector,
      this is 1 if we're doing an ellipsis.  Otherwise meaningless.  */
   unsigned ellipsis_p : 1;
@@ -2153,7 +2170,9 @@
   Lisp_Object *dpvec, *dpend;
 
   /* Length in bytes of the char that filled dpvec.  A value of zero
-     means that no such character is involved.  */
+     means that no such character is involved.  A negative value means
+     the rest of the line from the current iterator position onwards
+     is hidden by selective display or ellipsis.  */
   int dpvec_char_len;
 
   /* Face id to use for all characters in display vector.  -1 if unused. */
@@ -2245,10 +2264,13 @@
     Lisp_Object from_overlay;
     enum glyph_row_area area;
     enum it_method method;
+    bidi_dir_t paragraph_embedding;
     unsigned multibyte_p : 1;
     unsigned string_from_display_prop_p : 1;
     unsigned display_ellipsis_p : 1;
     unsigned avoid_cursor_p : 1;
+    unsigned bidi_p:1;
+    unsigned from_disp_prop_p : 1;
     enum line_wrap_method line_wrap;
 
     /* properties from display property that are reset by another display 
property. */
@@ -2469,7 +2491,7 @@
 
   /* Non-zero means we need to reorder bidirectional text for display
      in the visual order.  */
-  int bidi_p;
+  unsigned bidi_p : 1;
 
   /* For iterating over bidirectional text.  */
   struct bidi_it bidi_it;
@@ -2951,6 +2973,10 @@
 extern void bidi_move_to_visually_next (struct bidi_it *);
 extern void bidi_paragraph_init (bidi_dir_t, struct bidi_it *, int);
 extern int  bidi_mirror_char (int);
+extern void bidi_push_it (struct bidi_it *);
+extern void bidi_pop_it (struct bidi_it *);
+extern void *bidi_shelve_cache (void);
+extern void bidi_unshelve_cache (void *);
 
 /* Defined in xdisp.c */
 
@@ -3008,8 +3034,10 @@
 extern Lisp_Object lookup_glyphless_char_display (int, struct it *);
 extern int calc_pixel_width_or_height (double *, struct it *, Lisp_Object,
                                        struct font *, int, int *);
-extern EMACS_INT compute_display_string_pos (EMACS_INT, int);
-extern EMACS_INT compute_display_string_end (EMACS_INT);
+extern EMACS_INT compute_display_string_pos (struct text_pos *,
+                                            struct bidi_string_data *, int);
+extern EMACS_INT compute_display_string_end (EMACS_INT,
+                                            struct bidi_string_data *);
 
 #ifdef HAVE_WINDOW_SYSTEM
 

=== modified file 'src/dispnew.c'
--- a/src/dispnew.c     2011-07-10 08:20:10 +0000
+++ b/src/dispnew.c     2011-07-14 17:28:42 +0000
@@ -5275,10 +5275,12 @@
   struct image *img = 0;
 #endif
   int x0, x1, to_x;
+  void *itdata = NULL;
 
   /* We used to set current_buffer directly here, but that does the
      wrong thing with `face-remapping-alist' (bug#2044).  */
   Fset_buffer (w->buffer);
+  itdata = bidi_shelve_cache ();
   SET_TEXT_POS_FROM_MARKER (startp, w->start);
   CHARPOS (startp) = min (ZV, max (BEGV, CHARPOS (startp)));
   BYTEPOS (startp) = min (ZV_BYTE, max (BEGV_BYTE, BYTEPOS (startp)));
@@ -5312,6 +5314,7 @@
      argument is ZV to prevent move_it_in_display_line from matching
      based on buffer positions.  */
   move_it_in_display_line (&it, ZV, to_x, MOVE_TO_X);
+  bidi_unshelve_cache (itdata);
 
   Fset_buffer (old_current_buffer);
 

=== modified file 'src/indent.c'
--- a/src/indent.c      2011-07-14 08:30:34 +0000
+++ b/src/indent.c      2011-07-14 17:28:42 +0000
@@ -1989,6 +1989,7 @@
   struct gcpro gcpro1, gcpro2, gcpro3;
   Lisp_Object lcols = Qnil;
   double cols IF_LINT (= 0);
+  void *itdata = NULL;
 
   /* Allow LINES to be of the form (HPOS . VPOS) aka (COLUMNS . LINES).  */
   if (CONSP (lines) && (NUMBERP (XCAR (lines))))
@@ -2029,6 +2030,7 @@
       EMACS_INT it_start;
       int first_x, it_overshoot_expected IF_LINT (= 0);
 
+      itdata = bidi_shelve_cache ();
       SET_TEXT_POS (pt, PT, PT_BYTE);
       start_display (&it, w, pt);
       first_x = it.first_visible_x;
@@ -2133,6 +2135,7 @@
        }
 
       SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+      bidi_unshelve_cache (itdata);
     }
 
   if (BUFFERP (old_buffer))

=== modified file 'src/window.c'
--- a/src/window.c      2011-07-12 21:09:01 +0000
+++ b/src/window.c      2011-07-14 17:28:42 +0000
@@ -1351,6 +1351,7 @@
       struct text_pos startp;
       struct it it;
       struct buffer *old_buffer = NULL;
+      void *itdata = NULL;
 
       /* Cannot use Fvertical_motion because that function doesn't
         cope with variable-height lines.  */
@@ -1372,11 +1373,13 @@
       else
        SET_TEXT_POS_FROM_MARKER (startp, w->start);
 
+      itdata = bidi_shelve_cache ();
       start_display (&it, w, startp);
       move_it_vertically (&it, window_box_height (w));
       if (it.current_y < it.last_visible_y)
        move_it_past_eol (&it);
       value = make_number (IT_CHARPOS (it));
+      bidi_unshelve_cache (itdata);
 
       if (old_buffer)
        set_buffer_internal (old_buffer);
@@ -4238,6 +4241,7 @@
   /* True if we fiddled the window vscroll field without really scrolling.  */
   int vscrolled = 0;
   int x, y, rtop, rbot, rowh, vpos;
+  void *itdata = NULL;
 
   SET_TEXT_POS_FROM_MARKER (start, w->start);
 
@@ -4248,6 +4252,7 @@
 
   if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos))
     {
+      itdata = bidi_shelve_cache ();
       /* Move backward half the height of the window.  Performance note:
         vmotion used here is about 10% faster, but would give wrong
         results for variable height lines.  */
@@ -4268,6 +4273,7 @@
        }
 
       start = it.current.pos;
+      bidi_unshelve_cache (itdata);
     }
   else if (auto_window_vscroll_p)
     {
@@ -4330,6 +4336,7 @@
       Fset_window_vscroll (window, make_number (0), Qt);
     }
 
+  itdata = bidi_shelve_cache ();
   /* If scroll_preserve_screen_position is non-nil, we try to set
      point in the same window line as it is now, so get that line.  */
   if (!NILP (Vscroll_preserve_screen_position))
@@ -4408,12 +4415,16 @@
                            - it.current_y + it.max_ascent + it.max_descent);
              adjust_glyphs (it.f);
            }
-         else if (noerror)
-           return;
-         else if (n < 0)       /* could happen with empty buffers */
-           xsignal0 (Qbeginning_of_buffer);
          else
-           xsignal0 (Qend_of_buffer);
+           {
+             bidi_unshelve_cache (itdata);
+             if (noerror)
+               return;
+             else if (n < 0)   /* could happen with empty buffers */
+               xsignal0 (Qbeginning_of_buffer);
+             else
+               xsignal0 (Qend_of_buffer);
+           }
        }
       else
        {
@@ -4421,10 +4432,14 @@
            /* The first line was only partially visible, make it fully
               visible. */
            w->vscroll = 0;
-         else if (noerror)
-           return;
          else
-           xsignal0 (Qbeginning_of_buffer);
+           {
+             bidi_unshelve_cache (itdata);
+             if (noerror)
+               return;
+             else
+               xsignal0 (Qbeginning_of_buffer);
+           }
        }
 
       /* If control gets here, then we vscrolled.  */
@@ -4568,6 +4583,7 @@
            SET_PT_BOTH (charpos, bytepos);
        }
     }
+  bidi_unshelve_cache (itdata);
 }
 
 
@@ -4970,6 +4986,7 @@
   int height = window_box_height (w);
   struct buffer *old_buffer;
   int bottom_y;
+  void *itdata = NULL;
 
   if (XBUFFER (w->buffer) != current_buffer)
     {
@@ -4989,9 +5006,11 @@
   else
     SET_TEXT_POS_FROM_MARKER (start, w->start);
 
+  itdata = bidi_shelve_cache ();
   start_display (&it, w, start);
   move_it_vertically (&it, height);
   bottom_y = line_bottom_y (&it);
+  bidi_unshelve_cache (itdata);
 
   /* rms: On a non-window display,
      the value of it.vpos at the bottom of the screen
@@ -5090,12 +5109,14 @@
        {
          struct it it;
          struct text_pos pt;
+         void *itdata = bidi_shelve_cache ();
 
          SET_TEXT_POS (pt, PT, PT_BYTE);
          start_display (&it, w, pt);
          move_it_vertically_backward (&it, window_box_height (w) / 2);
          charpos = IT_CHARPOS (it);
          bytepos = IT_BYTEPOS (it);
+         bidi_unshelve_cache (itdata);
        }
       else if (iarg < 0)
        {
@@ -5104,6 +5125,7 @@
          int nlines = -iarg;
          int extra_line_spacing;
          int h = window_box_height (w);
+         void *itdata = bidi_shelve_cache ();
 
          iarg = - max (-iarg, this_scroll_margin);
 
@@ -5141,7 +5163,10 @@
              h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
            }
          if (h <= 0)
-           return Qnil;
+           {
+             bidi_unshelve_cache (itdata);
+             return Qnil;
+           }
 
          /* Now find the new top line (starting position) of the window.  */
          start_display (&it, w, pt);
@@ -5161,6 +5186,8 @@
 
          charpos = IT_CHARPOS (it);
          bytepos = IT_BYTEPOS (it);
+
+         bidi_unshelve_cache (itdata);
        }
       else
        {

=== modified file 'src/xdisp.c'
--- a/src/xdisp.c       2011-07-12 03:04:06 +0000
+++ b/src/xdisp.c       2011-07-14 17:28:42 +0000
@@ -129,9 +129,13 @@
    argument.
 
    Iteration over things to be displayed is then simple.  It is
-   started by initializing an iterator with a call to init_iterator.
-   Calls to get_next_display_element fill the iterator structure with
-   relevant information about the next thing to display.  Calls to
+   started by initializing an iterator with a call to init_iterator,
+   passing it the buffer position where to start iteration.  For
+   iteration over strings, pass -1 as the position to init_iterator,
+   and call reseat_to_string when the string is ready, to initialize
+   the iterator for that string.  Thereafter, calls to
+   get_next_display_element fill the iterator structure with relevant
+   information about the next thing to display.  Calls to
    set_iterator_to_next move the iterator to the next thing.
 
    Besides this, an iterator also contains information about the
@@ -590,6 +594,29 @@
 
 #define TEXT_PROP_DISTANCE_LIMIT 100
 
+/* SAVE_IT and RESTORE_IT are called when we save a snapshot of the
+   iterator state and later restore it.  This is needed because the
+   bidi iterator on bidi.c keeps a stacked cache of its states, which
+   is really a singleton.  When we use scratch iterator objects to
+   move around the buffer, we can cause the bidi cache to be pushed or
+   popped, and therefore we need to restore the cache state when we
+   return to the original iterator.  */
+#define SAVE_IT(ITCOPY,ITORIG,CACHE)           \
+  do {                                         \
+    if (CACHE)                                 \
+      xfree (CACHE);                           \
+    ITCOPY = ITORIG;                           \
+    CACHE = bidi_shelve_cache();               \
+  } while (0)
+
+#define RESTORE_IT(pITORIG,pITCOPY,CACHE)      \
+  do {                                         \
+    if (pITORIG != pITCOPY)                    \
+      *(pITORIG) = *(pITCOPY);                 \
+    bidi_unshelve_cache (CACHE);               \
+    CACHE = NULL;                              \
+  } while (0)
+
 #if GLYPH_DEBUG
 
 /* Non-zero means print traces of redisplay if compiled with
@@ -1195,6 +1222,7 @@
               int *rtop, int *rbot, int *rowh, int *vpos)
 {
   struct it it;
+  void *itdata = bidi_shelve_cache ();
   struct text_pos top;
   int visible_p = 0;
   struct buffer *old_buffer = NULL;
@@ -1225,13 +1253,21 @@
   move_it_to (&it, charpos, -1, it.last_visible_y-1, -1,
              (charpos >= 0 ? MOVE_TO_POS : 0) | MOVE_TO_Y);
 
-  if (charpos >= 0 && IT_CHARPOS (it) >= charpos)
+  if (charpos >= 0
+      && (((!it.bidi_p || it.bidi_it.scan_dir == 1)
+          && IT_CHARPOS (it) >= charpos)
+         /* When scanning backwards under bidi iteration, move_it_to
+            stops at or _before_ CHARPOS, because it stops at or to
+            the _right_ of the character at CHARPOS. */
+         || (it.bidi_p && it.bidi_it.scan_dir == -1
+             && IT_CHARPOS (it) <= charpos)))
     {
       /* We have reached CHARPOS, or passed it.  How the call to
-        move_it_to can overshoot: (i) If CHARPOS is on invisible
-        text, move_it_to stops at the end of the invisible text,
-        after CHARPOS.  (ii) If CHARPOS is in a display vector,
-        move_it_to stops on its last glyph.  */
+        move_it_to can overshoot: (i) If CHARPOS is on invisible text
+        or covered by a display property, move_it_to stops at the end
+        of the invisible text, to the right of CHARPOS.  (ii) If
+        CHARPOS is in a display vector, move_it_to stops on its last
+        glyph.  */
       int top_x = it.current_x;
       int top_y = it.current_y;
       enum it_method it_method = it.method;
@@ -1280,15 +1316,18 @@
     }
   else
     {
+      /* We were asked to provide info about WINDOW_END.  */
       struct it it2;
+      void *it2data = NULL;
 
-      it2 = it;
+      SAVE_IT (it2, it, it2data);
       if (IT_CHARPOS (it) < ZV && FETCH_BYTE (IT_BYTEPOS (it)) != '\n')
        move_it_by_lines (&it, 1);
       if (charpos < IT_CHARPOS (it)
          || (it.what == IT_EOB && charpos == IT_CHARPOS (it)))
        {
          visible_p = 1;
+         RESTORE_IT (&it2, &it2, it2data);
          move_it_to (&it2, charpos, -1, -1, -1, MOVE_TO_POS);
          *x = it2.current_x;
          *y = it2.current_y + it2.max_ascent - it2.ascent;
@@ -1301,7 +1340,10 @@
                                  WINDOW_HEADER_LINE_HEIGHT (w))));
          *vpos = it2.vpos;
        }
+      else
+       xfree (it2data);
     }
+  bidi_unshelve_cache (itdata);
 
   if (old_buffer)
     set_buffer_internal_1 (old_buffer);
@@ -2339,6 +2381,10 @@
   it->base_face_id = remapped_base_face_id;
   it->string = Qnil;
   IT_STRING_CHARPOS (*it) = IT_STRING_BYTEPOS (*it) = -1;
+  it->paragraph_embedding = L2R;
+  it->bidi_it.string.lstring = Qnil;
+  it->bidi_it.string.s = NULL;
+  it->bidi_it.string.bufpos = 0;
 
   /* The window in which we iterate over current_buffer:  */
   XSETWINDOW (it->window, w);
@@ -2395,13 +2441,6 @@
   /* Are multibyte characters enabled in current_buffer?  */
   it->multibyte_p = !NILP (BVAR (current_buffer, enable_multibyte_characters));
 
-  /* Do we need to reorder bidirectional text?  Not if this is a
-     unibyte buffer: by definition, none of the single-byte characters
-     are strong R2L, so no reordering is needed.  And bidi.c doesn't
-     support unibyte buffers anyway.  */
-  it->bidi_p
-    = !NILP (BVAR (current_buffer, bidi_display_reordering)) && 
it->multibyte_p;
-
   /* Non-zero if we should highlight the region.  */
   highlight_region_p
     = (!NILP (Vtransient_mark_mode)
@@ -2551,21 +2590,6 @@
        it->start_of_box_run_p = 1;
     }
 
-  /* If we are to reorder bidirectional text, init the bidi
-     iterator.  */
-  if (it->bidi_p)
-    {
-      /* Note the paragraph direction that this buffer wants to
-        use.  */
-      if (EQ (BVAR (current_buffer, bidi_paragraph_direction), Qleft_to_right))
-       it->paragraph_embedding = L2R;
-      else if (EQ (BVAR (current_buffer, bidi_paragraph_direction), 
Qright_to_left))
-       it->paragraph_embedding = R2L;
-      else
-       it->paragraph_embedding = NEUTRAL_DIR;
-      bidi_init_it (charpos, bytepos, FRAME_WINDOW_P (it->f), &it->bidi_it);
-    }
-
   /* If a buffer position was specified, set the iterator there,
      getting overlays and face properties from that position.  */
   if (charpos >= BUF_BEG (current_buffer))
@@ -2581,6 +2605,32 @@
        IT_BYTEPOS (*it) = bytepos;
 
       it->start = it->current;
+      /* Do we need to reorder bidirectional text?  Not if this is a
+        unibyte buffer: by definition, none of the single-byte
+        characters are strong R2L, so no reordering is needed.  And
+        bidi.c doesn't support unibyte buffers anyway.  */
+      it->bidi_p =
+       !NILP (BVAR (current_buffer, bidi_display_reordering))
+       && it->multibyte_p;
+
+      /* If we are to reorder bidirectional text, init the bidi
+        iterator.  */
+      if (it->bidi_p)
+       {
+         /* Note the paragraph direction that this buffer wants to
+            use.  */
+         if (EQ (BVAR (current_buffer, bidi_paragraph_direction),
+                 Qleft_to_right))
+           it->paragraph_embedding = L2R;
+         else if (EQ (BVAR (current_buffer, bidi_paragraph_direction),
+                      Qright_to_left))
+           it->paragraph_embedding = R2L;
+         else
+           it->paragraph_embedding = NEUTRAL_DIR;
+         bidi_unshelve_cache (NULL);
+         bidi_init_it (charpos, IT_BYTEPOS (*it), FRAME_WINDOW_P (it->f),
+                       &it->bidi_it);
+       }
 
       /* Compute faces etc.  */
       reseat (it, it->current.pos, 1);
@@ -2902,6 +2952,7 @@
                {
                  it->ignore_overlay_strings_at_pos_p = 1;
                  it->string_from_display_prop_p = 0;
+                 it->from_disp_prop_p = 0;
                  handle_overlay_change_p = 0;
                }
              handled = HANDLED_RECOMPUTE_PROPS;
@@ -3083,37 +3134,50 @@
   return endpos;
 }
 
-/* Return the character position of a display string at or after CHARPOS.
-   If no display string exists at or after CHARPOS, return ZV.  A
-   display string is either an overlay with `display' property whose
-   value is a string, or a `display' text property whose value is a
-   string.  FRAME_WINDOW_P is non-zero when we are displaying a window
+/* Return the character position of a display string at or after
+   position specified by POSITION.  If no display string exists at or
+   after POSITION, return ZV.  A display string is either an overlay
+   with `display' property whose value is a string, or a `display'
+   text property whose value is a string.  STRING is data about the
+   string to iterate; if STRING->lstring is nil, we are iterating a
+   buffer.  FRAME_WINDOW_P is non-zero when we are displaying a window
    on a GUI frame.  */
 EMACS_INT
-compute_display_string_pos (EMACS_INT charpos, int frame_window_p)
+compute_display_string_pos (struct text_pos *position,
+                           struct bidi_string_data *string, int frame_window_p)
 {
-  /* FIXME: Support display properties on strings (object = Qnil means
-     current buffer).  */
-  Lisp_Object object = Qnil;
+  /* OBJECT = nil means current buffer.  */
+  Lisp_Object object =
+    (string && STRINGP (string->lstring)) ? string->lstring : Qnil;
   Lisp_Object pos, spec;
-  struct text_pos position;
-  EMACS_INT bufpos;
+  int string_p = (string && (STRINGP (string->lstring) || string->s));
+  EMACS_INT eob = string_p ? string->schars : ZV;
+  EMACS_INT begb = string_p ? 0 : BEGV;
+  EMACS_INT bufpos, charpos = CHARPOS (*position);
+  struct text_pos tpos;
 
-  if (charpos >= ZV)
-    return ZV;
+  if (charpos >= eob
+      /* We don't support display properties whose values are strings
+        that have display string properties.  */
+      || string->from_disp_str
+      /* C strings cannot have display properties.  */
+      || (string->s && !STRINGP (object)))
+    return eob;
 
   /* If the character at CHARPOS is where the display string begins,
      return CHARPOS.  */
   pos = make_number (charpos);
-  CHARPOS (position) = charpos;
-  BYTEPOS (position) = CHAR_TO_BYTE (charpos);
-  bufpos = charpos;    /* FIXME! support strings as well */
+  if (STRINGP (object))
+    bufpos = string->bufpos;
+  else
+    bufpos = charpos;
+  tpos = *position;
   if (!NILP (spec = Fget_char_property (pos, Qdisplay, object))
-      && (charpos <= BEGV
+      && (charpos <= begb
          || !EQ (Fget_char_property (make_number (charpos - 1), Qdisplay,
                                      object),
                  spec))
-      && handle_display_spec (NULL, spec, object, Qnil, &position, bufpos,
+      && handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos,
                              frame_window_p))
     return charpos;
 
@@ -3121,17 +3185,21 @@
      that will replace the underlying text when displayed.  */
   do {
     pos = Fnext_single_char_property_change (pos, Qdisplay, object, Qnil);
-    CHARPOS (position) = XFASTINT (pos);
-    BYTEPOS (position) = CHAR_TO_BYTE (CHARPOS (position));
-    if (CHARPOS (position) >= ZV)
+    CHARPOS (tpos) = XFASTINT (pos);
+    if (STRINGP (object))
+      BYTEPOS (tpos) = string_char_to_byte (object, CHARPOS (tpos));
+    else
+      BYTEPOS (tpos) = CHAR_TO_BYTE (CHARPOS (tpos));
+    if (CHARPOS (tpos) >= eob)
       break;
     spec = Fget_char_property (pos, Qdisplay, object);
-    bufpos = CHARPOS (position);       /* FIXME! support strings as well */
+    if (!STRINGP (object))
+      bufpos = CHARPOS (tpos);
   } while (NILP (spec)
-          || !handle_display_spec (NULL, spec, object, Qnil, &position, bufpos,
+          || !handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos,
                                    frame_window_p));
 
-  return CHARPOS (position);
+  return CHARPOS (tpos);
 }
 
 /* Return the character position of the end of the display string that
@@ -3139,15 +3207,17 @@
    `display' property whose value is a string or a `display' text
    property whose value is a string.  */
 EMACS_INT
-compute_display_string_end (EMACS_INT charpos)
+compute_display_string_end (EMACS_INT charpos, struct bidi_string_data *string)
 {
-  /* FIXME: Support display properties on strings (object = Qnil means
-     current buffer).  */
-  Lisp_Object object = Qnil;
+  /* OBJECT = nil means current buffer.  */
+  Lisp_Object object =
+    (string && STRINGP (string->lstring)) ? string->lstring : Qnil;
   Lisp_Object pos = make_number (charpos);
+  EMACS_INT eob =
+    (STRINGP (object) || (string && string->s)) ? string->schars : ZV;
 
-  if (charpos >= ZV)
-    return ZV;
+  if (charpos >= eob || (string->s && !STRINGP (object)))
+    return eob;
 
   if (NILP (Fget_char_property (pos, Qdisplay, object)))
     abort ();
@@ -3440,21 +3510,23 @@
 
 
 /* Compute the face one character before or after the current position
-   of IT.  BEFORE_P non-zero means get the face in front of IT's
-   position.  Value is the id of the face.  */
+   of IT, in the visual order.  BEFORE_P non-zero means get the face
+   in front (to the left in L2R paragraphs, to the right in R2L
+   paragraphs) of IT's screen position.  Value is the ID of the face.  */
 
 static int
 face_before_or_after_it_pos (struct it *it, int before_p)
 {
   int face_id, limit;
   EMACS_INT next_check_charpos;
-  struct text_pos pos;
+  struct it it_copy;
+  void *it_copy_data = NULL;
 
   xassert (it->s == NULL);
 
   if (STRINGP (it->string))
     {
-      EMACS_INT bufpos;
+      EMACS_INT bufpos, charpos;
       int base_face_id;
 
       /* No face change past the end of the string (for the case
@@ -3464,16 +3536,59 @@
          || (IT_STRING_CHARPOS (*it) == 0 && before_p))
        return it->face_id;
 
-      /* Set pos to the position before or after IT's current position.  */
-      if (before_p)
-       pos = string_pos (IT_STRING_CHARPOS (*it) - 1, it->string);
+      if (!it->bidi_p)
+       {
+         /* Set charpos to the position before or after IT's current
+            position, in the logical order, which in the non-bidi
+            case is the same as the visual order.  */
+         if (before_p)
+           charpos = IT_STRING_CHARPOS (*it) - 1;
+         else if (it->what == IT_COMPOSITION)
+           /* For composition, we must check the character after the
+              composition.  */
+           charpos = IT_STRING_CHARPOS (*it) + it->cmp_it.nchars;
+         else
+           charpos = IT_STRING_CHARPOS (*it) + 1;
+       }
       else
-       /* For composition, we must check the character after the
-           composition.  */
-       pos = (it->what == IT_COMPOSITION
-              ? string_pos (IT_STRING_CHARPOS (*it)
-                            + it->cmp_it.nchars, it->string)
-              : string_pos (IT_STRING_CHARPOS (*it) + 1, it->string));
+       {
+         if (before_p)
+           {
+             /* With bidi iteration, the character before the current
+                in the visual order cannot be found by simple
+                iteration, because "reverse" reordering is not
+                supported.  Instead, we need to use the move_it_*
+                family of functions.  */
+             /* Ignore face changes before the first visible
+                character on this display line.  */
+             if (it->current_x <= it->first_visible_x)
+               return it->face_id;
+             SAVE_IT (it_copy, *it, it_copy_data);
+             /* Implementation note: Since move_it_in_display_line
+                works in the iterator geometry, and thinks the first
+                character is always the leftmost, even in R2L lines,
+                we don't need to distinguish between the R2L and L2R
+                cases here.  */
+             move_it_in_display_line (&it_copy, SCHARS (it_copy.string),
+                                      it_copy.current_x - 1, MOVE_TO_X);
+             charpos = IT_STRING_CHARPOS (it_copy);
+             RESTORE_IT (it, it, it_copy_data);
+           }
+         else
+           {
+             /* Set charpos to the string position of the character
+                that comes after IT's current position in the visual
+                order.  */
+             int n = (it->what == IT_COMPOSITION ? it->cmp_it.nchars : 1);
+
+             it_copy = *it;
+             while (n--)
+               bidi_move_to_visually_next (&it_copy.bidi_it);
+
+             charpos = it_copy.bidi_it.charpos;
+           }
+       }
+      xassert (0 <= charpos && charpos <= SCHARS (it->string));
 
       if (it->current.overlay_string_index >= 0)
        bufpos = IT_CHARPOS (*it);
@@ -3485,7 +3600,7 @@
       /* Get the face for ASCII, or unibyte.  */
       face_id = face_at_string_position (it->w,
                                         it->string,
-                                        CHARPOS (pos),
+                                        charpos,
                                         bufpos,
                                         it->region_beg_charpos,
                                         it->region_end_charpos,
@@ -3497,16 +3612,19 @@
         suitable for unibyte text if IT->string is unibyte.  */
       if (STRING_MULTIBYTE (it->string))
        {
-         const unsigned char *p = SDATA (it->string) + BYTEPOS (pos);
+         struct text_pos pos1 = string_pos (charpos, it->string);
+         const unsigned char *p = SDATA (it->string) + BYTEPOS (pos1);
          int c, len;
          struct face *face = FACE_FROM_ID (it->f, face_id);
 
          c = string_char_and_length (p, &len);
-         face_id = FACE_FOR_CHAR (it->f, face, c, CHARPOS (pos), it->string);
+         face_id = FACE_FOR_CHAR (it->f, face, c, charpos, it->string);
        }
     }
   else
     {
+      struct text_pos pos;
+
       if ((IT_CHARPOS (*it) >= ZV && !before_p)
          || (IT_CHARPOS (*it) <= BEGV && before_p))
        return it->face_id;
@@ -3514,17 +3632,63 @@
       limit = IT_CHARPOS (*it) + TEXT_PROP_DISTANCE_LIMIT;
       pos = it->current.pos;
 
-      if (before_p)
-       DEC_TEXT_POS (pos, it->multibyte_p);
+      if (!it->bidi_p)
+       {
+         if (before_p)
+           DEC_TEXT_POS (pos, it->multibyte_p);
+         else
+           {
+             if (it->what == IT_COMPOSITION)
+               {
+                 /* For composition, we must check the position after
+                    the composition.  */
+                 pos.charpos += it->cmp_it.nchars;
+                 pos.bytepos += it->len;
+               }
+             else
+               INC_TEXT_POS (pos, it->multibyte_p);
+           }
+       }
       else
        {
-         if (it->what == IT_COMPOSITION)
-           /* For composition, we must check the position after the
-              composition.  */
-           pos.charpos += it->cmp_it.nchars, pos.bytepos += it->len;
+         if (before_p)
+           {
+             /* With bidi iteration, the character before the current
+                in the visual order cannot be found by simple
+                iteration, because "reverse" reordering is not
+                supported.  Instead, we need to use the move_it_*
+                family of functions.  */
+             /* Ignore face changes before the first visible
+                character on this display line.  */
+             if (it->current_x <= it->first_visible_x)
+               return it->face_id;
+             SAVE_IT (it_copy, *it, it_copy_data);
+             /* Implementation note: Since move_it_in_display_line
+                works in the iterator geometry, and thinks the first
+                character is always the leftmost, even in R2L lines,
+                we don't need to distinguish between the R2L and L2R
+                cases here.  */
+             move_it_in_display_line (&it_copy, ZV,
+                                      it_copy.current_x - 1, MOVE_TO_X);
+             pos = it_copy.current.pos;
+             RESTORE_IT (it, it, it_copy_data);
+           }
          else
-           INC_TEXT_POS (pos, it->multibyte_p);
+           {
+             /* Set charpos to the buffer position of the character
+                that comes after IT's current position in the visual
+                order.  */
+             int n = (it->what == IT_COMPOSITION ? it->cmp_it.nchars : 1);
+
+             it_copy = *it;
+             while (n--)
+               bidi_move_to_visually_next (&it_copy.bidi_it);
+
+             SET_TEXT_POS (pos,
+                           it_copy.bidi_it.charpos, it_copy.bidi_it.bytepos);
+           }
        }
+      xassert (BEGV <= CHARPOS (pos) && CHARPOS (pos) <= ZV);
 
       /* Determine face for CHARSET_ASCII, or unibyte.  */
       face_id = face_at_buffer_position (it->w,
@@ -3575,6 +3739,8 @@
       if (!NILP (prop)
          && IT_STRING_CHARPOS (*it) < it->end_charpos)
        {
+         EMACS_INT endpos;
+
          handled = HANDLED_RECOMPUTE_PROPS;
 
          /* Get the position at which the next change of the
@@ -3589,12 +3755,37 @@
             change in the property is at position end_charpos.
             Move IT's current position to that position.  */
          if (INTEGERP (end_charpos)
-             && XFASTINT (end_charpos) < XFASTINT (limit))
+             && (endpos = XFASTINT (end_charpos)) < XFASTINT (limit))
            {
              struct text_pos old;
+             EMACS_INT oldpos;
+
              old = it->current.string_pos;
-             IT_STRING_CHARPOS (*it) = XFASTINT (end_charpos);
-             compute_string_pos (&it->current.string_pos, old, it->string);
+             oldpos = CHARPOS (old);
+             if (it->bidi_p)
+               {
+                 if (it->bidi_it.first_elt
+                     && it->bidi_it.charpos < SCHARS (it->string))
+                   bidi_paragraph_init (it->paragraph_embedding,
+                                        &it->bidi_it, 1);
+                 /* Bidi-iterate out of the invisible text.  */
+                 do
+                   {
+                     bidi_move_to_visually_next (&it->bidi_it);
+                   }
+                 while (oldpos <= it->bidi_it.charpos
+                        && it->bidi_it.charpos < endpos);
+
+                 IT_STRING_CHARPOS (*it) = it->bidi_it.charpos;
+                 IT_STRING_BYTEPOS (*it) = it->bidi_it.bytepos;
+                 if (IT_CHARPOS (*it) >= endpos)
+                   it->prev_stop = endpos;
+               }
+             else
+               {
+                 IT_STRING_CHARPOS (*it) = XFASTINT (end_charpos);
+                 compute_string_pos (&it->current.string_pos, old, it->string);
+               }
            }
          else
            {
@@ -4244,6 +4435,7 @@
          it->method = GET_FROM_IMAGE;
          it->from_overlay = Qnil;
          it->face_id = face_id;
+         it->from_disp_prop_p = 1;
 
          /* Say that we haven't consumed the characters with
             `display' property yet.  The call to pop_it in
@@ -4316,6 +4508,7 @@
         when we are finished with the glyph property value.  */
       push_it (it, position);
       it->from_overlay = overlay;
+      it->from_disp_prop_p = 1;
 
       if (NILP (location))
        it->area = TEXT_AREA;
@@ -4333,12 +4526,34 @@
          it->end_charpos = it->string_nchars = SCHARS (it->string);
          it->method = GET_FROM_STRING;
          it->stop_charpos = 0;
+         it->prev_stop = 0;
+         it->base_level_stop = 0;
          it->string_from_display_prop_p = 1;
          /* Say that we haven't consumed the characters with
             `display' property yet.  The call to pop_it in
             set_iterator_to_next will clean this up.  */
          if (BUFFERP (object))
            *position = start_pos;
+
+         /* Force paragraph direction to be that of the parent
+            object.  If the parent object's paragraph direction is
+            not yet determined, default to L2R.  */
+         if (it->bidi_p && it->bidi_it.paragraph_dir == R2L)
+           it->paragraph_embedding = it->bidi_it.paragraph_dir;
+         else
+           it->paragraph_embedding = L2R;
+
+         /* Set up the bidi iterator for this display string.  */
+         if (it->bidi_p)
+           {
+             it->bidi_it.string.lstring = it->string;
+             it->bidi_it.string.s = NULL;
+             it->bidi_it.string.schars = it->end_charpos;
+             it->bidi_it.string.bufpos = bufpos;
+             it->bidi_it.string.from_disp_str = 1;
+             it->bidi_it.string.unibyte = !it->multibyte_p;
+             bidi_init_it (0, 0, FRAME_WINDOW_P (it->f), &it->bidi_it);
+           }
        }
       else if (CONSP (value) && EQ (XCAR (value), Qspace))
        {
@@ -4695,6 +4910,20 @@
       it->stop_charpos = 0;
       if (it->cmp_it.stop_pos >= 0)
        it->cmp_it.stop_pos = 0;
+      it->prev_stop = 0;
+      it->base_level_stop = 0;
+
+      /* Set up the bidi iterator for this overlay string.  */
+      if (it->bidi_p)
+       {
+         it->bidi_it.string.lstring = it->string;
+         it->bidi_it.string.s = NULL;
+         it->bidi_it.string.schars = SCHARS (it->string);
+         it->bidi_it.string.bufpos = it->overlay_strings_charpos;
+         it->bidi_it.string.from_disp_str = it->string_from_display_prop_p;
+         it->bidi_it.string.unibyte = !it->multibyte_p;
+         bidi_init_it (0, 0, FRAME_WINDOW_P (it->f), &it->bidi_it);
+       }
     }
 
   CHECK_IT (it);
@@ -4961,8 +5190,32 @@
       it->stop_charpos = 0;
       xassert (STRINGP (it->string));
       it->end_charpos = SCHARS (it->string);
+      it->prev_stop = 0;
+      it->base_level_stop = 0;
       it->multibyte_p = STRING_MULTIBYTE (it->string);
       it->method = GET_FROM_STRING;
+      it->from_disp_prop_p = 0;
+
+      /* Force paragraph direction to be that of the parent
+        buffer.  */
+      if (it->bidi_p && it->bidi_it.paragraph_dir == R2L)
+       it->paragraph_embedding = it->bidi_it.paragraph_dir;
+      else
+       it->paragraph_embedding = L2R;
+
+      /* Set up the bidi iterator for this overlay string.  */
+      if (it->bidi_p)
+       {
+         EMACS_INT pos = (charpos > 0 ? charpos : IT_CHARPOS (*it));
+
+         it->bidi_it.string.lstring = it->string;
+         it->bidi_it.string.s = NULL;
+         it->bidi_it.string.schars = SCHARS (it->string);
+         it->bidi_it.string.bufpos = pos;
+         it->bidi_it.string.from_disp_str = it->string_from_display_prop_p;
+         it->bidi_it.string.unibyte = !it->multibyte_p;
+         bidi_init_it (0, 0, FRAME_WINDOW_P (it->f), &it->bidi_it);
+       }
       return 1;
     }
 
@@ -5037,19 +5290,30 @@
   p->string_from_display_prop_p = it->string_from_display_prop_p;
   p->display_ellipsis_p = 0;
   p->line_wrap = it->line_wrap;
+  p->bidi_p = it->bidi_p;
+  p->paragraph_embedding = it->paragraph_embedding;
+  p->from_disp_prop_p = it->from_disp_prop_p;
   ++it->sp;
+
+  /* Save the state of the bidi iterator as well. */
+  if (it->bidi_p)
+    bidi_push_it (&it->bidi_it);
 }
 
 static void
 iterate_out_of_display_property (struct it *it)
 {
+  int buffer_p = BUFFERP (it->object);
+  EMACS_INT eob = (buffer_p ? ZV : it->end_charpos);
+  EMACS_INT bob = (buffer_p ? BEGV : 0);
+
   /* Maybe initialize paragraph direction.  If we are at the beginning
      of a new paragraph, next_element_from_buffer may not have a
      chance to do that.  */
-  if (it->bidi_it.first_elt && it->bidi_it.charpos < ZV)
+  if (it->bidi_it.first_elt && it->bidi_it.charpos < eob)
     bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, 1);
   /* prev_stop can be zero, so check against BEGV as well.  */
-  while (it->bidi_it.charpos >= BEGV
+  while (it->bidi_it.charpos >= bob
         && it->prev_stop <= it->bidi_it.charpos
         && it->bidi_it.charpos < CHARPOS (it->position))
     bidi_move_to_visually_next (&it->bidi_it);
@@ -5063,7 +5327,10 @@
     {
       SET_TEXT_POS (it->position,
                    it->bidi_it.charpos, it->bidi_it.bytepos);
-      it->current.pos = it->position;
+      if (buffer_p)
+       it->current.pos = it->position;
+      else
+       it->current.string_pos = it->position;
     }
 }
 
@@ -5077,6 +5344,7 @@
 pop_it (struct it *it)
 {
   struct iterator_stack_entry *p;
+  int from_display_prop = it->from_disp_prop_p;
 
   xassert (it->sp > 0);
   --it->sp;
@@ -5101,22 +5369,10 @@
       it->slice = p->u.image.slice;
       break;
     case GET_FROM_STRETCH:
-      it->object = p->u.comp.object;
+      it->object = p->u.stretch.object;
       break;
     case GET_FROM_BUFFER:
       it->object = it->w->buffer;
-      if (it->bidi_p)
-       {
-         /* Bidi-iterate until we get out of the portion of text, if
-            any, covered by a `display' text property or an overlay
-            with `display' property.  (We cannot just jump there,
-            because the internal coherency of the bidi iterator state
-            can not be preserved across such jumps.)  We also must
-            determine the paragraph base direction if the overlay we
-            just processed is at the beginning of a new
-            paragraph.  */
-         iterate_out_of_display_property (it);
-       }
       break;
     case GET_FROM_STRING:
       it->object = it->string;
@@ -5142,6 +5398,30 @@
   it->voffset = p->voffset;
   it->string_from_display_prop_p = p->string_from_display_prop_p;
   it->line_wrap = p->line_wrap;
+  it->bidi_p = p->bidi_p;
+  it->paragraph_embedding = p->paragraph_embedding;
+  it->from_disp_prop_p = p->from_disp_prop_p;
+  if (it->bidi_p)
+    {
+      bidi_pop_it (&it->bidi_it);
+      /* Bidi-iterate until we get out of the portion of text, if any,
+        covered by a `display' text property or by an overlay with
+        `display' property.  (We cannot just jump there, because the
+        internal coherency of the bidi iterator state can not be
+        preserved across such jumps.)  We also must determine the
+        paragraph base direction if the overlay we just processed is
+        at the beginning of a new paragraph.  */
+      if (from_display_prop
+         && (it->method == GET_FROM_BUFFER || it->method == GET_FROM_STRING))
+       iterate_out_of_display_property (it);
+
+      xassert ((BUFFERP (it->object)
+               && IT_CHARPOS (*it) == it->bidi_it.charpos
+               && IT_BYTEPOS (*it) == it->bidi_it.bytepos)
+              || (STRINGP (it->object)
+                  && IT_STRING_CHARPOS (*it) == it->bidi_it.charpos
+                  && IT_STRING_BYTEPOS (*it) == it->bidi_it.bytepos));
+    }
 }
 
 
@@ -5225,15 +5505,16 @@
 
       xassert (!STRINGP (it->string));
 
-      /* If there isn't any `display' property in sight, and no
-        overlays, we can just use the position of the newline in
-        buffer text.  */
-      if (it->stop_charpos >= limit
-         || ((pos = Fnext_single_property_change (make_number (start),
-                                                  Qdisplay,
-                                                  Qnil, make_number (limit)),
-              NILP (pos))
-             && next_overlay_change (start) == ZV))
+      /* If we are not bidi-reordering, and there isn't any `display'
+        property in sight, and no overlays, we can just use the
+        position of the newline in buffer text.  */
+      if (!it->bidi_p
+         && (it->stop_charpos >= limit
+             || ((pos = Fnext_single_property_change (make_number (start),
+                                                      Qdisplay, Qnil,
+                                                      make_number (limit)),
+                  NILP (pos))
+                 && next_overlay_change (start) == ZV)))
        {
          IT_CHARPOS (*it) = limit;
          IT_BYTEPOS (*it) = CHAR_TO_BYTE (limit);
@@ -5291,10 +5572,13 @@
 
       {
        struct it it2;
+       void *it2data = NULL;
        EMACS_INT pos;
        EMACS_INT beg, end;
        Lisp_Object val, overlay;
 
+       SAVE_IT (it2, *it, it2data);
+
        /* If newline is part of a composition, continue from start of 
composition */
        if (find_composition (IT_CHARPOS (*it), -1, &beg, &end, &val, Qnil)
            && beg < IT_CHARPOS (*it))
@@ -5302,20 +5586,25 @@
 
        /* If newline is replaced by a display property, find start of overlay
           or interval and continue search from that point.  */
-       it2 = *it;
        pos = --IT_CHARPOS (it2);
        --IT_BYTEPOS (it2);
        it2.sp = 0;
+       bidi_unshelve_cache (NULL);
        it2.string_from_display_prop_p = 0;
+       it2.from_disp_prop_p = 0;
        if (handle_display_prop (&it2) == HANDLED_RETURN
            && !NILP (val = get_char_property_and_overlay
                      (make_number (pos), Qdisplay, Qnil, &overlay))
            && (OVERLAYP (overlay)
                ? (beg = OVERLAY_POSITION (OVERLAY_START (overlay)))
                : get_property_and_range (pos, Qdisplay, &val, &beg, &end, 
Qnil)))
-         goto replaced;
+         {
+           RESTORE_IT (it, it, it2data);
+           goto replaced;
+         }
 
        /* Newline is not replaced by anything -- so we are done.  */
+       RESTORE_IT (it, it, it2data);
        break;
 
       replaced:
@@ -5382,14 +5671,29 @@
        {
          if (IT_STRING_CHARPOS (*it) > 0)
            {
-             --IT_STRING_CHARPOS (*it);
-             --IT_STRING_BYTEPOS (*it);
+             if (!it->bidi_p)
+               {
+                 --IT_STRING_CHARPOS (*it);
+                 --IT_STRING_BYTEPOS (*it);
+               }
+             else
+               /* Setting this flag will cause
+                  bidi_move_to_visually_next not to advance, but
+                  instead deliver the current character (newline),
+                  which is what the ON_NEWLINE_P flag wants.  */
+               it->bidi_it.first_elt = 1;
            }
        }
       else if (IT_CHARPOS (*it) > BEGV)
        {
-         --IT_CHARPOS (*it);
-         --IT_BYTEPOS (*it);
+         if (!it->bidi_p)
+           {
+             --IT_CHARPOS (*it);
+             --IT_BYTEPOS (*it);
+           }
+         /* With bidi iteration, the call to `reseat' will cause
+            bidi_move_to_visually_next deliver the current character,
+            the newline, instead of advancing.  */
          reseat (it, it->current.pos, 0);
        }
     }
@@ -5471,19 +5775,24 @@
   IT_STRING_CHARPOS (*it) = -1;
   IT_STRING_BYTEPOS (*it) = -1;
   it->string = Qnil;
-  it->string_from_display_prop_p = 0;
   it->method = GET_FROM_BUFFER;
   it->object = it->w->buffer;
   it->area = TEXT_AREA;
   it->multibyte_p = !NILP (BVAR (current_buffer, enable_multibyte_characters));
   it->sp = 0;
   it->string_from_display_prop_p = 0;
+  it->from_disp_prop_p = 0;
   it->face_before_selective_p = 0;
   if (it->bidi_p)
     {
-      it->bidi_it.first_elt = 1;
+      bidi_init_it (IT_CHARPOS (*it), IT_BYTEPOS (*it), FRAME_WINDOW_P (it->f),
+                   &it->bidi_it);
+      bidi_unshelve_cache (NULL);
       it->bidi_it.paragraph_dir = NEUTRAL_DIR;
-      it->bidi_it.disp_pos = -1;
+      it->bidi_it.string.s = NULL;
+      it->bidi_it.string.lstring = Qnil;
+      it->bidi_it.string.bufpos = 0;
+      it->bidi_it.string.unibyte = 0;
     }
 
   if (set_stop_p)
@@ -5534,6 +5843,10 @@
   if (multibyte >= 0)
     it->multibyte_p = multibyte > 0;
 
+  /* Bidirectional reordering of strings is controlled by the default
+     value of bidi-display-reordering.  */
+  it->bidi_p = !NILP (BVAR (&buffer_defaults, bidi_display_reordering));
+
   if (s == NULL)
     {
       xassert (STRINGP (string));
@@ -5542,6 +5855,18 @@
       it->end_charpos = it->string_nchars = SCHARS (string);
       it->method = GET_FROM_STRING;
       it->current.string_pos = string_pos (charpos, string);
+
+      if (it->bidi_p)
+       {
+         it->bidi_it.string.lstring = string;
+         it->bidi_it.string.s = NULL;
+         it->bidi_it.string.schars = it->end_charpos;
+         it->bidi_it.string.bufpos = 0;
+         it->bidi_it.string.from_disp_str = 0;
+         it->bidi_it.string.unibyte = !it->multibyte_p;
+         bidi_init_it (charpos, IT_STRING_BYTEPOS (*it),
+                       FRAME_WINDOW_P (it->f), &it->bidi_it);
+       }
     }
   else
     {
@@ -5562,13 +5887,28 @@
          it->end_charpos = it->string_nchars = strlen (s);
        }
 
+      if (it->bidi_p)
+       {
+         it->bidi_it.string.lstring = Qnil;
+         it->bidi_it.string.s = s;
+         it->bidi_it.string.schars = it->end_charpos;
+         it->bidi_it.string.bufpos = 0;
+         it->bidi_it.string.from_disp_str = 0;
+         it->bidi_it.string.unibyte = !it->multibyte_p;
+         bidi_init_it (charpos, IT_BYTEPOS (*it), FRAME_WINDOW_P (it->f),
+                       &it->bidi_it);
+       }
       it->method = GET_FROM_C_STRING;
     }
 
   /* PRECISION > 0 means don't return more than PRECISION characters
      from the string.  */
   if (precision > 0 && it->end_charpos - charpos > precision)
-    it->end_charpos = it->string_nchars = charpos + precision;
+    {
+      it->end_charpos = it->string_nchars = charpos + precision;
+      if (it->bidi_p)
+       it->bidi_it.string.schars = it->end_charpos;
+    }
 
   /* FIELD_WIDTH > 0 means pad with spaces until FIELD_WIDTH
      characters have been returned.  FIELD_WIDTH == 0 means don't pad,
@@ -5576,6 +5916,9 @@
      padding with `-' at the end of a mode line.  */
   if (field_width < 0)
     field_width = INFINITY;
+  /* Implementation note: We deliberately don't enlarge
+     it->bidi_it.string.schars here to fit it->end_charpos, because
+     the bidi iterator cannot produce characters out of thin air.  */
   if (field_width > it->end_charpos - charpos)
     it->end_charpos = charpos + field_width;
 
@@ -5584,6 +5927,14 @@
     it->dp = XCHAR_TABLE (Vstandard_display_table);
 
   it->stop_charpos = charpos;
+  it->prev_stop = charpos;
+  it->base_level_stop = 0;
+  if (it->bidi_p)
+    {
+      it->bidi_it.first_elt = 1;
+      it->bidi_it.paragraph_dir = NEUTRAL_DIR;
+      it->bidi_it.disp_pos = -1;
+    }
   if (s == NULL && it->multibyte_p)
     {
       EMACS_INT endpos = SCHARS (it->string);
@@ -6218,8 +6569,22 @@
 
     case GET_FROM_C_STRING:
       /* Current display element of IT is from a C string.  */
-      IT_BYTEPOS (*it) += it->len;
-      IT_CHARPOS (*it) += 1;
+      if (!it->bidi_p
+         /* If the string position is beyond string's end, it means
+            next_element_from_c_string is padding the string with
+            blanks, in which case we bypass the bidi iterator,
+            because it cannot deal with such virtual characters.  */
+         || IT_CHARPOS (*it) >= it->bidi_it.string.schars)
+       {
+         IT_BYTEPOS (*it) += it->len;
+         IT_CHARPOS (*it) += 1;
+       }
+      else
+       {
+         bidi_move_to_visually_next (&it->bidi_it);
+         IT_BYTEPOS (*it) = it->bidi_it.bytepos;
+         IT_CHARPOS (*it) = it->bidi_it.charpos;
+       }
       break;
 
     case GET_FROM_DISPLAY_VECTOR:
@@ -6273,23 +6638,95 @@
       xassert (it->s == NULL && STRINGP (it->string));
       if (it->cmp_it.id >= 0)
        {
-         IT_STRING_CHARPOS (*it) += it->cmp_it.nchars;
-         IT_STRING_BYTEPOS (*it) += it->cmp_it.nbytes;
-         if (it->cmp_it.to < it->cmp_it.nglyphs)
-           it->cmp_it.from = it->cmp_it.to;
+         int i;
+
+         if (! it->bidi_p)
+           {
+             IT_STRING_CHARPOS (*it) += it->cmp_it.nchars;
+             IT_STRING_BYTEPOS (*it) += it->cmp_it.nbytes;
+             if (it->cmp_it.to < it->cmp_it.nglyphs)
+               it->cmp_it.from = it->cmp_it.to;
+             else
+               {
+                 it->cmp_it.id = -1;
+                 composition_compute_stop_pos (&it->cmp_it,
+                                               IT_STRING_CHARPOS (*it),
+                                               IT_STRING_BYTEPOS (*it),
+                                               it->end_charpos, it->string);
+               }
+           }
+         else if (! it->cmp_it.reversed_p)
+           {
+             for (i = 0; i < it->cmp_it.nchars; i++)
+               bidi_move_to_visually_next (&it->bidi_it);
+             IT_STRING_BYTEPOS (*it) = it->bidi_it.bytepos;
+             IT_STRING_CHARPOS (*it) = it->bidi_it.charpos;
+
+             if (it->cmp_it.to < it->cmp_it.nglyphs)
+               it->cmp_it.from = it->cmp_it.to;
+             else
+               {
+                 EMACS_INT stop = it->end_charpos;
+                 if (it->bidi_it.scan_dir < 0)
+                   stop = -1;
+                 composition_compute_stop_pos (&it->cmp_it,
+                                               IT_STRING_CHARPOS (*it),
+                                               IT_STRING_BYTEPOS (*it), stop,
+                                               it->string);
+               }
+           }
          else
            {
-             it->cmp_it.id = -1;
-             composition_compute_stop_pos (&it->cmp_it,
-                                           IT_STRING_CHARPOS (*it),
-                                           IT_STRING_BYTEPOS (*it),
-                                           it->end_charpos, it->string);
+             for (i = 0; i < it->cmp_it.nchars; i++)
+               bidi_move_to_visually_next (&it->bidi_it);
+             IT_STRING_BYTEPOS (*it) = it->bidi_it.bytepos;
+             IT_STRING_CHARPOS (*it) = it->bidi_it.charpos;
+             if (it->cmp_it.from > 0)
+               it->cmp_it.to = it->cmp_it.from;
+             else
+               {
+                 EMACS_INT stop = it->end_charpos;
+                 if (it->bidi_it.scan_dir < 0)
+                   stop = -1;
+                 composition_compute_stop_pos (&it->cmp_it,
+                                               IT_STRING_CHARPOS (*it),
+                                               IT_STRING_BYTEPOS (*it), stop,
+                                               it->string);
+               }
            }
        }
       else
        {
-         IT_STRING_BYTEPOS (*it) += it->len;
-         IT_STRING_CHARPOS (*it) += 1;
+         if (!it->bidi_p
+             /* If the string position is beyond string's end, it
+                means next_element_from_string is padding the string
+                with blanks, in which case we bypass the bidi
+                iterator, because it cannot deal with such virtual
+                characters.  */
+             || IT_STRING_CHARPOS (*it) >= it->bidi_it.string.schars)
+           {
+             IT_STRING_BYTEPOS (*it) += it->len;
+             IT_STRING_CHARPOS (*it) += 1;
+           }
+         else
+           {
+             int prev_scan_dir = it->bidi_it.scan_dir;
+
+             bidi_move_to_visually_next (&it->bidi_it);
+             IT_STRING_BYTEPOS (*it) = it->bidi_it.bytepos;
+             IT_STRING_CHARPOS (*it) = it->bidi_it.charpos;
+             if (prev_scan_dir != it->bidi_it.scan_dir)
+               {
+                 EMACS_INT stop = it->end_charpos;
+
+                 if (it->bidi_it.scan_dir < 0)
+                   stop = -1;
+                 composition_compute_stop_pos (&it->cmp_it,
+                                               IT_STRING_CHARPOS (*it),
+                                               IT_STRING_BYTEPOS (*it), stop,
+                                               it->string);
+               }
+           }
        }
 
     consider_string_end:
@@ -6395,6 +6832,107 @@
   return 1;
 }
 
+/* Get the first element of string/buffer in the visual order, after
+   being reseated to a new position in a string or a buffer.  */
+static void
+get_visually_first_element (struct it *it)
+{
+  int string_p = STRINGP (it->string) || it->s;
+  EMACS_INT eob = (string_p ? it->bidi_it.string.schars : ZV);
+  EMACS_INT bob = (string_p ? 0 : BEGV);
+
+  if (STRINGP (it->string))
+    {
+      it->bidi_it.charpos = IT_STRING_CHARPOS (*it);
+      it->bidi_it.bytepos = IT_STRING_BYTEPOS (*it);
+    }
+  else
+    {
+      it->bidi_it.charpos = IT_CHARPOS (*it);
+      it->bidi_it.bytepos = IT_BYTEPOS (*it);
+    }
+
+  if (it->bidi_it.charpos == eob)
+    {
+      /* Nothing to do, but reset the FIRST_ELT flag, like
+        bidi_paragraph_init does, because we are not going to
+        call it.  */
+      it->bidi_it.first_elt = 0;
+    }
+  else if (it->bidi_it.charpos == bob
+          || (!string_p
+              /* FIXME: Should support all Unicode line separators.  */
+              && (FETCH_CHAR (it->bidi_it.bytepos - 1) == '\n'
+                  || FETCH_CHAR (it->bidi_it.bytepos) == '\n')))
+    {
+      /* If we are at the beginning of a line/string, we can produce
+        the next element right away.  */
+      bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, 1);
+      bidi_move_to_visually_next (&it->bidi_it);
+    }
+  else
+    {
+      EMACS_INT orig_bytepos = it->bidi_it.bytepos;
+
+      /* We need to prime the bidi iterator starting at the line's or
+        string's beginning, before we will be able to produce the
+        next element.  */
+      if (string_p)
+       it->bidi_it.charpos = it->bidi_it.bytepos = 0;
+      else
+       {
+         it->bidi_it.charpos = find_next_newline_no_quit (IT_CHARPOS (*it),
+                                                          -1);
+         it->bidi_it.bytepos = CHAR_TO_BYTE (it->bidi_it.charpos);
+       }
+      bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, 1);
+      do
+       {
+         /* Now return to buffer/string position where we were asked
+            to get the next display element, and produce that.  */
+         bidi_move_to_visually_next (&it->bidi_it);
+       }
+      while (it->bidi_it.bytepos != orig_bytepos
+            && it->bidi_it.charpos < eob);
+    }
+
+  /*  Adjust IT's position information to where we ended up.  */
+  if (STRINGP (it->string))
+    {
+      IT_STRING_CHARPOS (*it) = it->bidi_it.charpos;
+      IT_STRING_BYTEPOS (*it) = it->bidi_it.bytepos;
+    }
+  else
+    {
+      IT_CHARPOS (*it) = it->bidi_it.charpos;
+      IT_BYTEPOS (*it) = it->bidi_it.bytepos;
+    }
+
+  if (STRINGP (it->string) || !it->s)
+    {
+      EMACS_INT stop, charpos, bytepos;
+
+      if (STRINGP (it->string))
+       {
+         xassert (!it->s);
+         stop = SCHARS (it->string);
+         if (stop > it->end_charpos)
+           stop = it->end_charpos;
+         charpos = IT_STRING_CHARPOS (*it);
+         bytepos = IT_STRING_BYTEPOS (*it);
+       }
+      else
+       {
+         stop = it->end_charpos;
+         charpos = IT_CHARPOS (*it);
+         bytepos = IT_BYTEPOS (*it);
+       }
+      if (it->bidi_it.scan_dir < 0)
+       stop = -1;
+      composition_compute_stop_pos (&it->cmp_it, charpos, bytepos, stop,
+                                   it->string);
+    }
+}
 
 /* Load IT with the next display element from Lisp string IT->string.
    IT->current.string_pos is the current position within the string.
@@ -6407,18 +6945,79 @@
   struct text_pos position;
 
   xassert (STRINGP (it->string));
+  xassert (!it->bidi_p || it->string == it->bidi_it.string.lstring);
   xassert (IT_STRING_CHARPOS (*it) >= 0);
   position = it->current.string_pos;
 
+  /* With bidi reordering, the character to display might not be the
+     character at IT_STRING_CHARPOS.  BIDI_IT.FIRST_ELT non-zero means
+     that we were reseat()ed to a new string, whose paragraph
+     direction is not known.  */
+  if (it->bidi_p && it->bidi_it.first_elt)
+    {
+      get_visually_first_element (it);
+      SET_TEXT_POS (position, IT_STRING_CHARPOS (*it), IT_STRING_BYTEPOS 
(*it));
+    }
+
   /* Time to check for invisible text?  */
-  if (IT_STRING_CHARPOS (*it) < it->end_charpos
-      && IT_STRING_CHARPOS (*it) == it->stop_charpos)
+  if (IT_STRING_CHARPOS (*it) < it->end_charpos)
     {
-      handle_stop (it);
+      if (IT_STRING_CHARPOS (*it) >= it->stop_charpos)
+       {
+         if (!(!it->bidi_p
+               || BIDI_AT_BASE_LEVEL (it->bidi_it)
+               || IT_STRING_CHARPOS (*it) == it->stop_charpos))
+           {
+             /* With bidi non-linear iteration, we could find
+                ourselves far beyond the last computed stop_charpos,
+                with several other stop positions in between that we
+                missed.  Scan them all now, in buffer's logical
+                order, until we find and handle the last stop_charpos
+                that precedes our current position.  */
+             handle_stop_backwards (it, it->stop_charpos);
+             return GET_NEXT_DISPLAY_ELEMENT (it);
+           }
+         else
+           {
+             if (it->bidi_p)
+               {
+                 /* Take note of the stop position we just moved
+                    across, for when we will move back across it.  */
+                 it->prev_stop = it->stop_charpos;
+                 /* If we are at base paragraph embedding level, take
+                    note of the last stop position seen at this
+                    level.  */
+                 if (BIDI_AT_BASE_LEVEL (it->bidi_it))
+                   it->base_level_stop = it->stop_charpos;
+               }
+             handle_stop (it);
 
-      /* Since a handler may have changed IT->method, we must
-        recurse here.  */
-      return GET_NEXT_DISPLAY_ELEMENT (it);
+             /* Since a handler may have changed IT->method, we must
+                recurse here.  */
+             return GET_NEXT_DISPLAY_ELEMENT (it);
+           }
+       }
+      else if (it->bidi_p
+              /* If we are before prev_stop, we may have overstepped
+                 on our way backwards a stop_pos, and if so, we need
+                 to handle that stop_pos.  */
+              && IT_STRING_CHARPOS (*it) < it->prev_stop
+              /* We can sometimes back up for reasons that have nothing
+                 to do with bidi reordering.  E.g., compositions.  The
+                 code below is only needed when we are above the base
+                 embedding level, so test for that explicitly.  */
+              && !BIDI_AT_BASE_LEVEL (it->bidi_it))
+       {
+         /* If we lost track of base_level_stop, we have no better place
+            for handle_stop_backwards to start from than BEGV.  This
+            happens, e.g., when we were reseated to the previous
+            screenful of text by vertical-motion.  */
+         if (it->base_level_stop <= 0
+             || IT_STRING_CHARPOS (*it) < it->base_level_stop)
+           it->base_level_stop = 0;
+         handle_stop_backwards (it, it->base_level_stop);
+         return GET_NEXT_DISPLAY_ELEMENT (it);
+       }
     }
 
   if (it->current.overlay_string_index >= 0)
@@ -6432,7 +7031,10 @@
          return 0;
        }
       else if (CHAR_COMPOSED_P (it, IT_STRING_CHARPOS (*it),
-                               IT_STRING_BYTEPOS (*it), SCHARS (it->string))
+                               IT_STRING_BYTEPOS (*it),
+                               it->bidi_it.scan_dir < 0
+                               ? -1
+                               : SCHARS (it->string))
               && next_element_from_composition (it))
        {
          return 1;
@@ -6467,7 +7069,10 @@
          CHARPOS (position) = BYTEPOS (position) = -1;
        }
       else if (CHAR_COMPOSED_P (it, IT_STRING_CHARPOS (*it),
-                               IT_STRING_BYTEPOS (*it), it->string_nchars)
+                               IT_STRING_BYTEPOS (*it),
+                               it->bidi_it.scan_dir < 0
+                               ? -1
+                               : it->string_nchars)
               && next_element_from_composition (it))
        {
          return 1;
@@ -6506,12 +7111,20 @@
   int success_p = 1;
 
   xassert (it->s);
+  xassert (!it->bidi_p || it->s == it->bidi_it.string.s);
   it->what = IT_CHARACTER;
   BYTEPOS (it->position) = CHARPOS (it->position) = 0;
   it->object = Qnil;
 
-  /* IT's position can be greater IT->string_nchars in case a field
-     width or precision has been specified when the iterator was
+  /* With bidi reordering, the character to display might not be the
+     character at IT_CHARPOS.  BIDI_IT.FIRST_ELT non-zero means that
+     we were reseated to a new string, whose paragraph direction is
+     not known.  */
+  if (it->bidi_p && it->bidi_it.first_elt)
+    get_visually_first_element (it);
+
+  /* IT's position can be greater than IT->string_nchars in case a
+     field width or precision has been specified when the iterator was
      initialized.  */
   if (IT_CHARPOS (*it) >= it->end_charpos)
     {
@@ -6586,18 +7199,19 @@
   return 1;
 }
 
-/* Scan forward from CHARPOS in the current buffer, until we find a
-   stop position > current IT's position.  Then handle the stop
+/* Scan forward from CHARPOS in the current buffer/string, until we
+   find a stop position > current IT's position.  Then handle the stop
    position before that.  This is called when we bump into a stop
    position while reordering bidirectional text.  CHARPOS should be
-   the last previously processed stop_pos (or BEGV, if none were
+   the last previously processed stop_pos (or BEGV/0, if none were
    processed yet) whose position is less that IT's current
    position.  */
 
 static void
 handle_stop_backwards (struct it *it, EMACS_INT charpos)
 {
-  EMACS_INT where_we_are = IT_CHARPOS (*it);
+  int bufp = !STRINGP (it->string);
+  EMACS_INT where_we_are = (bufp ? IT_CHARPOS (*it) : IT_STRING_CHARPOS (*it));
   struct display_pos save_current = it->current;
   struct text_pos save_position = it->position;
   struct text_pos pos1;
@@ -6608,8 +7222,13 @@
   do
     {
       it->prev_stop = charpos;
-      SET_TEXT_POS (pos1, charpos, CHAR_TO_BYTE (charpos));
-      reseat_1 (it, pos1, 0);
+      if (bufp)
+       {
+         SET_TEXT_POS (pos1, charpos, CHAR_TO_BYTE (charpos));
+         reseat_1 (it, pos1, 0);
+       }
+      else
+       it->current.string_pos = string_pos (charpos, it->string);
       compute_stop_pos (it);
       /* We must advance forward, right?  */
       if (it->stop_charpos <= it->prev_stop)
@@ -6638,6 +7257,10 @@
   int success_p = 1;
 
   xassert (IT_CHARPOS (*it) >= BEGV);
+  xassert (NILP (it->string) && !it->s);
+  xassert (!it->bidi_p
+          || (it->bidi_it.string.lstring == Qnil
+              && it->bidi_it.string.s == NULL));
 
   /* With bidi reordering, the character to display might not be the
      character at IT_CHARPOS.  BIDI_IT.FIRST_ELT non-zero means that
@@ -6645,59 +7268,8 @@
      a different paragraph.  */
   if (it->bidi_p && it->bidi_it.first_elt)
     {
-      it->bidi_it.charpos = IT_CHARPOS (*it);
-      it->bidi_it.bytepos = IT_BYTEPOS (*it);
-      if (it->bidi_it.bytepos == ZV_BYTE)
-       {
-         /* Nothing to do, but reset the FIRST_ELT flag, like
-            bidi_paragraph_init does, because we are not going to
-            call it.  */
-         it->bidi_it.first_elt = 0;
-       }
-      else if (it->bidi_it.bytepos == BEGV_BYTE
-         /* FIXME: Should support all Unicode line separators.  */
-         || FETCH_CHAR (it->bidi_it.bytepos - 1) == '\n'
-         || FETCH_CHAR (it->bidi_it.bytepos) == '\n')
-       {
-         /* If we are at the beginning of a line, we can produce the
-            next element right away.  */
-         bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, 1);
-         bidi_move_to_visually_next (&it->bidi_it);
-       }
-      else
-       {
-         EMACS_INT orig_bytepos = IT_BYTEPOS (*it);
-
-         /* We need to prime the bidi iterator starting at the line's
-            beginning, before we will be able to produce the next
-            element.  */
-         IT_CHARPOS (*it) = find_next_newline_no_quit (IT_CHARPOS (*it), -1);
-         IT_BYTEPOS (*it) = CHAR_TO_BYTE (IT_CHARPOS (*it));
-         it->bidi_it.charpos = IT_CHARPOS (*it);
-         it->bidi_it.bytepos = IT_BYTEPOS (*it);
-         bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, 1);
-         do
-           {
-             /* Now return to buffer position where we were asked to
-                get the next display element, and produce that.  */
-             bidi_move_to_visually_next (&it->bidi_it);
-           }
-         while (it->bidi_it.bytepos != orig_bytepos
-                && it->bidi_it.bytepos < ZV_BYTE);
-       }
-
-      it->bidi_it.first_elt = 0; /* paranoia: bidi.c does this */
-      /*  Adjust IT's position information to where we ended up.  */
-      IT_CHARPOS (*it) = it->bidi_it.charpos;
-      IT_BYTEPOS (*it) = it->bidi_it.bytepos;
+      get_visually_first_element (it);
       SET_TEXT_POS (it->position, IT_CHARPOS (*it), IT_BYTEPOS (*it));
-      {
-       EMACS_INT stop = it->end_charpos;
-       if (it->bidi_it.scan_dir < 0)
-         stop = -1;
-       composition_compute_stop_pos (&it->cmp_it, IT_CHARPOS (*it),
-                                     IT_BYTEPOS (*it), stop, Qnil);
-      }
     }
 
   if (IT_CHARPOS (*it) >= it->stop_charpos)
@@ -6756,17 +7328,23 @@
        }
     }
   else if (it->bidi_p
+          /* If we are before prev_stop, we may have overstepped on
+             our way backwards a stop_pos, and if so, we need to
+             handle that stop_pos.  */
+          && IT_CHARPOS (*it) < it->prev_stop
           /* We can sometimes back up for reasons that have nothing
              to do with bidi reordering.  E.g., compositions.  The
              code below is only needed when we are above the base
              embedding level, so test for that explicitly.  */
-          && !BIDI_AT_BASE_LEVEL (it->bidi_it)
-          && IT_CHARPOS (*it) < it->prev_stop)
+          && !BIDI_AT_BASE_LEVEL (it->bidi_it))
     {
-      if (it->base_level_stop <= 0)
+      /* If we lost track of base_level_stop, we have no better place
+        for handle_stop_backwards to start from than BEGV.  This
+        happens, e.g., when we were reseated to the previous
+        screenful of text by vertical-motion.  */
+      if (it->base_level_stop <= 0
+         || IT_CHARPOS (*it) < it->base_level_stop)
        it->base_level_stop = BEGV;
-      if (IT_CHARPOS (*it) < it->base_level_stop)
-       abort ();
       handle_stop_backwards (it, it->base_level_stop);
       return GET_NEXT_DISPLAY_ELEMENT (it);
     }
@@ -6970,9 +7548,11 @@
   enum move_it_result result = MOVE_UNDEFINED;
   struct glyph_row *saved_glyph_row;
   struct it wrap_it, atpos_it, atx_it;
+  void *wrap_data = NULL, *atpos_data = NULL, *atx_data = NULL;
   int may_wrap = 0;
   enum it_method prev_method = it->method;
   EMACS_INT prev_pos = IT_CHARPOS (*it);
+  int saw_smaller_pos = prev_pos < to_charpos;
 
   /* Don't produce glyphs in produce_glyphs.  */
   saved_glyph_row = it->glyph_row;
@@ -7013,15 +7593,16 @@
   ((IT)->current_x = x, (IT)->max_ascent = ascent,     \
    (IT)->max_descent = descent)
 
-      /* Stop if we move beyond TO_CHARPOS (after an image or stretch
-        glyph).  */
+      /* Stop if we move beyond TO_CHARPOS (after an image or a
+        display string or stretch glyph).  */
       if ((op & MOVE_TO_POS) != 0
          && BUFFERP (it->object)
          && it->method == GET_FROM_BUFFER
          && ((!it->bidi_p && IT_CHARPOS (*it) > to_charpos)
              || (it->bidi_p
                  && (prev_method == GET_FROM_IMAGE
-                     || prev_method == GET_FROM_STRETCH)
+                     || prev_method == GET_FROM_STRETCH
+                     || prev_method == GET_FROM_STRING)
                  /* Passed TO_CHARPOS from left to right.  */
                  && ((prev_pos < to_charpos
                       && IT_CHARPOS (*it) > to_charpos)
@@ -7038,12 +7619,9 @@
            /* If wrap_it is valid, the current position might be in a
               word that is wrapped.  So, save the iterator in
               atpos_it and continue to see if wrapping happens.  */
-           atpos_it = *it;
+           SAVE_IT (atpos_it, *it, atpos_data);
        }
 
-      prev_method = it->method;
-      if (it->method == GET_FROM_BUFFER)
-       prev_pos = IT_CHARPOS (*it);
       /* Stop when ZV reached.
          We used to stop here when TO_CHARPOS reached as well, but that is
          too soon if this glyph does not fit on this line.  So we handle it
@@ -7075,18 +7653,18 @@
                     already found, we are done.  */
                  if (atpos_it.sp >= 0)
                    {
-                     *it = atpos_it;
+                     RESTORE_IT (it, &atpos_it, atpos_data);
                      result = MOVE_POS_MATCH_OR_ZV;
                      goto done;
                    }
                  if (atx_it.sp >= 0)
                    {
-                     *it = atx_it;
+                     RESTORE_IT (it, &atx_it, atx_data);
                      result = MOVE_X_REACHED;
                      goto done;
                    }
                  /* Otherwise, we can wrap here.  */
-                 wrap_it = *it;
+                 SAVE_IT (wrap_it, *it, wrap_data);
                  may_wrap = 0;
                }
            }
@@ -7107,6 +7685,9 @@
 
       if (it->area != TEXT_AREA)
        {
+         prev_method = it->method;
+         if (it->method == GET_FROM_BUFFER)
+           prev_pos = IT_CHARPOS (*it);
          set_iterator_to_next (it, 1);
          if (IT_CHARPOS (*it) < CHARPOS (this_line_min_pos))
            SET_TEXT_POS (this_line_min_pos,
@@ -7154,7 +7735,7 @@
                        goto buffer_pos_reached;
                      if (atpos_it.sp < 0)
                        {
-                         atpos_it = *it;
+                         SAVE_IT (atpos_it, *it, atpos_data);
                          IT_RESET_X_ASCENT_DESCENT (&atpos_it);
                        }
                    }
@@ -7168,7 +7749,7 @@
                        }
                      if (atx_it.sp < 0)
                        {
-                         atx_it = *it;
+                         SAVE_IT (atx_it, *it, atx_data);
                          IT_RESET_X_ASCENT_DESCENT (&atx_it);
                        }
                    }
@@ -7212,12 +7793,15 @@
                              if (it->line_wrap == WORD_WRAP
                                  && atpos_it.sp < 0)
                                {
-                                 atpos_it = *it;
+                                 SAVE_IT (atpos_it, *it, atpos_data);
                                  atpos_it.current_x = x_before_this_char;
                                  atpos_it.hpos = hpos_before_this_char;
                                }
                            }
 
+                         prev_method = it->method;
+                         if (it->method == GET_FROM_BUFFER)
+                           prev_pos = IT_CHARPOS (*it);
                          set_iterator_to_next (it, 1);
                          if (IT_CHARPOS (*it) < CHARPOS (this_line_min_pos))
                            SET_TEXT_POS (this_line_min_pos,
@@ -7257,7 +7841,7 @@
 
                  if (wrap_it.sp >= 0)
                    {
-                     *it = wrap_it;
+                     RESTORE_IT (it, &wrap_it, wrap_data);
                      atpos_it.sp = -1;
                      atx_it.sp = -1;
                    }
@@ -7274,7 +7858,7 @@
                    goto buffer_pos_reached;
                  if (it->line_wrap == WORD_WRAP && atpos_it.sp < 0)
                    {
-                     atpos_it = *it;
+                     SAVE_IT (atpos_it, *it, atpos_data);
                      IT_RESET_X_ASCENT_DESCENT (&atpos_it);
                    }
                }
@@ -7311,10 +7895,20 @@
       /* Is this a line end?  If yes, we're done.  */
       if (ITERATOR_AT_END_OF_LINE_P (it))
        {
-         result = MOVE_NEWLINE_OR_CR;
+         /* If we are past TO_CHARPOS, but never saw any character
+            positions smaller than TO_CHARPOS, return
+            MOVE_POS_MATCH_OR_ZV, like the unidirectional display
+            did.  */
+         if ((op & MOVE_TO_POS) != 0
+             && !saw_smaller_pos
+             && IT_CHARPOS (*it) > to_charpos)
+           result = MOVE_POS_MATCH_OR_ZV;
+         else
+           result = MOVE_NEWLINE_OR_CR;
          break;
        }
 
+      prev_method = it->method;
       if (it->method == GET_FROM_BUFFER)
        prev_pos = IT_CHARPOS (*it);
       /* The current display element has been consumed.  Advance
@@ -7322,6 +7916,8 @@
       set_iterator_to_next (it, 1);
       if (IT_CHARPOS (*it) < CHARPOS (this_line_min_pos))
        SET_TEXT_POS (this_line_min_pos, IT_CHARPOS (*it), IT_BYTEPOS (*it));
+      if (IT_CHARPOS (*it) < to_charpos)
+       saw_smaller_pos = 1;
 
       /* Stop if lines are truncated and IT's current x-position is
         past the right edge of the window now.  */
@@ -7354,12 +7950,19 @@
   /* If we scanned beyond to_pos and didn't find a point to wrap at,
      restore the saved iterator.  */
   if (atpos_it.sp >= 0)
-    *it = atpos_it;
+    RESTORE_IT (it, &atpos_it, atpos_data);
   else if (atx_it.sp >= 0)
-    *it = atx_it;
+    RESTORE_IT (it, &atx_it, atx_data);
 
  done:
 
+  if (atpos_data)
+    xfree (atpos_data);
+  if (atx_data)
+    xfree (atx_data);
+  if (wrap_data)
+    xfree (wrap_data);
+
   /* Restore the iterator settings altered at the beginning of this
      function.  */
   it->glyph_row = saved_glyph_row;
@@ -7375,8 +7978,12 @@
   if (it->line_wrap == WORD_WRAP
       && (op & MOVE_TO_X))
     {
-      struct it save_it = *it;
-      int skip = move_it_in_display_line_to (it, to_charpos, to_x, op);
+      struct it save_it;
+      void *save_data = NULL;
+      int skip;
+
+      SAVE_IT (save_it, *it, save_data);
+      skip = move_it_in_display_line_to (it, to_charpos, to_x, op);
       /* When word-wrap is on, TO_X may lie past the end
         of a wrapped line.  Then it->current is the
         character on the next line, so backtrack to the
@@ -7384,10 +7991,12 @@
       if (skip == MOVE_LINE_CONTINUED)
        {
          int prev_x = max (it->current_x - 1, 0);
-         *it = save_it;
+         RESTORE_IT (it, &save_it, save_data);
          move_it_in_display_line_to
            (it, -1, prev_x, MOVE_TO_X);
        }
+      else
+       xfree (save_data);
     }
   else
     move_it_in_display_line_to (it, to_charpos, to_x, op);
@@ -7402,14 +8011,15 @@
    description of enum move_operation_enum.
 
    If TO_CHARPOS is in invisible text, e.g. a truncated part of a
-   screen line, this function will set IT to the next position >
-   TO_CHARPOS.  */
+   screen line, this function will set IT to the next position that is
+   displayed to the right of TO_CHARPOS on the screen.  */
 
 void
 move_it_to (struct it *it, EMACS_INT to_charpos, int to_x, int to_y, int 
to_vpos, int op)
 {
   enum move_it_result skip, skip2 = MOVE_X_REACHED;
   int line_height, line_start_x = 0, reached = 0;
+  void *backup_data = NULL;
 
   for (;;)
     {
@@ -7462,7 +8072,7 @@
          struct it it_backup;
 
          if (it->line_wrap == WORD_WRAP)
-           it_backup = *it;
+           SAVE_IT (it_backup, *it, backup_data);
 
          /* TO_Y specified means stop at TO_X in the line containing
             TO_Y---or at TO_CHARPOS if this is reached first.  The
@@ -7496,7 +8106,7 @@
                  reached = 6;
                  break;
                }
-             it_backup = *it;
+             SAVE_IT (it_backup, *it, backup_data);
              TRACE_MOVE ((stderr, "move_it: from %d\n", IT_CHARPOS (*it)));
              skip2 = move_it_in_display_line_to (it, to_charpos, -1,
                                                  op & MOVE_TO_POS);
@@ -7510,7 +8120,7 @@
                  /* If TO_Y is in this line and TO_X was reached
                     above, we scanned too far.  We have to restore
                     IT's settings to the ones before skipping.  */
-                 *it = it_backup;
+                 RESTORE_IT (it, &it_backup, backup_data);
                  reached = 6;
                }
              else
@@ -7537,7 +8147,7 @@
                      && it->line_wrap == WORD_WRAP)
                    {
                      int prev_x = max (it->current_x - 1, 0);
-                     *it = it_backup;
+                     RESTORE_IT (it, &it_backup, backup_data);
                      skip = move_it_in_display_line_to
                        (it, -1, prev_x, MOVE_TO_X);
                    }
@@ -7644,6 +8254,9 @@
       last_max_ascent = it->max_ascent;
     }
 
+  if (backup_data)
+    xfree (backup_data);
+
   TRACE_MOVE ((stderr, "move_it_to: reached %d\n", reached));
 }
 
@@ -7661,6 +8274,7 @@
 {
   int nlines, h;
   struct it it2, it3;
+  void *it2data = NULL, *it3data = NULL;
   EMACS_INT start_pos;
 
  move_further_back:
@@ -7689,7 +8303,7 @@
      start of the next line so that we get its height.  We need this
      height to be able to tell whether we reached the specified
      y-distance.  */
-  it2 = *it;
+  SAVE_IT (it2, *it, it2data);
   it2.max_ascent = it2.max_descent = 0;
   do
     {
@@ -7698,7 +8312,7 @@
     }
   while (!IT_POS_VALID_AFTER_MOVE_P (&it2));
   xassert (IT_CHARPOS (*it) >= BEGV);
-  it3 = it2;
+  SAVE_IT (it3, it2, it3data);
 
   move_it_to (&it2, start_pos, -1, -1, -1, MOVE_TO_POS);
   xassert (IT_CHARPOS (*it) >= BEGV);
@@ -7717,8 +8331,10 @@
     {
       /* DY == 0 means move to the start of the screen line.  The
         value of nlines is > 0 if continuation lines were involved.  */
+      RESTORE_IT (it, it, it2data);
       if (nlines > 0)
        move_it_by_lines (it, nlines);
+      xfree (it3data);
     }
   else
     {
@@ -7726,9 +8342,13 @@
         Note that H has been subtracted in front of the if-statement.  */
       int target_y = it->current_y + h - dy;
       int y0 = it3.current_y;
-      int y1 = line_bottom_y (&it3);
-      int line_height = y1 - y0;
+      int y1;
+      int line_height;
 
+      RESTORE_IT (&it3, &it3, it3data);
+      y1 = line_bottom_y (&it3);
+      line_height = y1 - y0;
+      RESTORE_IT (it, it, it2data);
       /* If we did not reach target_y, try to move further backward if
         we can.  If we moved too far backward, try to move forward.  */
       if (target_y < it->current_y
@@ -7855,6 +8475,7 @@
   else
     {
       struct it it2;
+      void *it2data = NULL;
       EMACS_INT start_charpos, i;
 
       /* Start at the beginning of the screen line containing IT's
@@ -7890,7 +8511,7 @@
 
       /* Above call may have moved too far if continuation lines
         are involved.  Scan forward and see if it did.  */
-      it2 = *it;
+      SAVE_IT (it2, *it, it2data);
       it2.vpos = it2.current_y = 0;
       move_it_to (&it2, start_charpos, -1, -1, -1, MOVE_TO_POS);
       it->vpos -= it2.vpos;
@@ -7901,12 +8522,18 @@
       if (it2.vpos > -dvpos)
        {
          int delta = it2.vpos + dvpos;
-         it2 = *it;
+
+         RESTORE_IT (&it2, &it2, it2data);
+         SAVE_IT (it2, *it, it2data);
          move_it_to (it, -1, -1, -1, it->vpos + delta, MOVE_TO_VPOS);
          /* Move back again if we got too far ahead.  */
          if (IT_CHARPOS (*it) >= start_charpos)
-           *it = it2;
+           RESTORE_IT (it, &it2, it2data);
+         else
+           xfree (it2data);
        }
+      else
+       RESTORE_IT (it, it, it2data);
     }
 }
 
@@ -10393,7 +11020,7 @@
          ++i;
        }
 
-      /* Stop at line ends.  */
+      /* Stop at line end.  */
       if (ITERATOR_AT_END_OF_LINE_P (it))
        break;
 
@@ -10476,6 +11103,7 @@
   it.first_visible_x = 0;
   it.last_visible_x = FRAME_TOTAL_COLS (f) * FRAME_COLUMN_WIDTH (f);
   reseat_to_string (&it, NULL, f->desired_tool_bar_string, 0, 0, 0, -1);
+  it.paragraph_embedding = L2R;
 
   while (!ITERATOR_AT_END_P (&it))
     {
@@ -10558,6 +11186,14 @@
   /* Build a string that represents the contents of the tool-bar.  */
   build_desired_tool_bar_string (f);
   reseat_to_string (&it, NULL, f->desired_tool_bar_string, 0, 0, 0, -1);
+  /* FIXME: This should be controlled by a user option.  But it
+     doesn't make sense to have an R2L tool bar if the menu bar cannot
+     be drawn also R2L, and making the menu bar R2L is tricky due
+     toolkit-specific code that implements it.  If an R2L tool bar is
+     ever supported, display_tool_bar_line should also be augmented to
+     call unproduce_glyphs like display_line and display_string
+     do.  */
+  it.paragraph_embedding = L2R;
 
   if (f->n_tool_bar_rows == 0)
     {
@@ -13245,14 +13881,18 @@
             which was computed as distance from window bottom to
             point.  This matters when lines at window top and lines
             below window bottom have different height.  */
-         struct it it1 = it;
+         struct it it1;
+         void *it1data = NULL;
          /* We use a temporary it1 because line_bottom_y can modify
             its argument, if it moves one line down; see there.  */
-         int start_y = line_bottom_y (&it1);
+         int start_y;
 
+         SAVE_IT (it1, it, it1data);
+         start_y = line_bottom_y (&it1);
          do {
+           RESTORE_IT (&it, &it, it1data);
            move_it_by_lines (&it, 1);
-           it1 = it;
+           SAVE_IT (it1, it, it1data);
          } while (line_bottom_y (&it1) - start_y < amount_to_scroll);
        }
 
@@ -14360,10 +15000,13 @@
          && BEGV <= CHARPOS (startp) && CHARPOS (startp) <= ZV)
        {
          struct it it1;
+         void *it1data = NULL;
 
+         SAVE_IT (it1, it, it1data);
          start_display (&it1, w, startp);
          move_it_vertically (&it1, margin);
          margin_pos = IT_CHARPOS (it1);
+         RESTORE_IT (&it, &it, it1data);
        }
       scrolling_up = PT > margin_pos;
       aggressive =
@@ -17229,6 +17872,8 @@
 static int
 push_display_prop (struct it *it, Lisp_Object prop)
 {
+  xassert (it->method == GET_FROM_BUFFER);
+
   push_it (it, NULL);
 
   if (STRINGP (prop))
@@ -17246,6 +17891,29 @@
       it->end_charpos = it->string_nchars = SCHARS (it->string);
       it->method = GET_FROM_STRING;
       it->stop_charpos = 0;
+      it->prev_stop = 0;
+      it->base_level_stop = 0;
+      it->string_from_display_prop_p = 1;
+      it->from_disp_prop_p = 1;
+
+      /* Force paragraph direction to be that of the parent
+        buffer.  */
+      if (it->bidi_p && it->bidi_it.paragraph_dir == R2L)
+       it->paragraph_embedding = it->bidi_it.paragraph_dir;
+      else
+       it->paragraph_embedding = L2R;
+
+      /* Set up the bidi iterator for this display string.  */
+      if (it->bidi_p)
+       {
+         it->bidi_it.string.lstring = it->string;
+         it->bidi_it.string.s = NULL;
+         it->bidi_it.string.schars = it->end_charpos;
+         it->bidi_it.string.bufpos = IT_CHARPOS (*it);
+         it->bidi_it.string.from_disp_str = 1;
+         it->bidi_it.string.unibyte = !it->multibyte_p;
+         bidi_init_it (0, 0, FRAME_WINDOW_P (it->f), &it->bidi_it);
+       }
     }
   else if (CONSP (prop) && EQ (XCAR (prop), Qspace))
     {
@@ -17292,6 +17960,7 @@
 handle_line_prefix (struct it *it)
 {
   Lisp_Object prefix;
+
   if (it->continuation_lines_width > 0)
     {
       prefix = get_it_property (it, Qwrap_prefix);
@@ -17317,9 +17986,9 @@
 
 
 /* Remove N glyphs at the start of a reversed IT->glyph_row.  Called
-   only for R2L lines from display_line, when it decides that too many
-   glyphs were produced by PRODUCE_GLYPHS, and the line needs to be
-   continued.  */
+   only for R2L lines from display_line and display_string, when they
+   decide that too many glyphs were produced by PRODUCE_GLYPHS, and
+   the line/string needs to be continued on the next glyph row.  */
 static void
 unproduce_glyphs (struct it *it, int n)
 {
@@ -17349,12 +18018,13 @@
      lines' rows is implemented for bidi-reordered rows.  */
 
   /* ROW->minpos is the value of min_pos, the minimal buffer position
-     we have in ROW.  */
-  if (min_pos <= ZV)
+     we have in ROW, or ROW->start.pos if that is smaller.  */
+  if (min_pos <= ZV && min_pos < row->start.pos.charpos)
     SET_TEXT_POS (row->minpos, min_pos, min_bpos);
   else
-    /* We didn't find _any_ valid buffer positions in any of the
-       glyphs, so we must trust the iterator's computed positions.  */
+    /* We didn't find buffer positions smaller than ROW->start, or
+       didn't find _any_ valid buffer positions in any of the glyphs,
+       so we must trust the iterator's computed positions.  */
       row->minpos = row->start.pos;
   if (max_pos <= 0)
     {
@@ -17429,6 +18099,7 @@
   struct glyph_row *row = it->glyph_row;
   Lisp_Object overlay_arrow_string;
   struct it wrap_it;
+  void *wrap_data = NULL;
   int may_wrap = 0, wrap_x IF_LINT (= 0);
   int wrap_row_used = -1;
   int wrap_row_ascent IF_LINT (= 0), wrap_row_height IF_LINT (= 0);
@@ -17583,7 +18254,7 @@
                may_wrap = 1;
              else if (may_wrap)
                {
-                 wrap_it = *it;
+                 SAVE_IT (wrap_it, *it, wrap_data);
                  wrap_x = x;
                  wrap_row_used = row->used[TEXT_AREA];
                  wrap_row_ascent = row->ascent;
@@ -17753,7 +18424,7 @@
                      if (row->reversed_p)
                        unproduce_glyphs (it,
                                          row->used[TEXT_AREA] - wrap_row_used);
-                     *it = wrap_it;
+                     RESTORE_IT (it, &wrap_it, wrap_data);
                      it->continuation_lines_width += wrap_x;
                      row->used[TEXT_AREA] = wrap_row_used;
                      row->ascent = wrap_row_ascent;
@@ -18159,6 +18830,8 @@
       itb.charpos = pos;
       itb.bytepos = bytepos;
       itb.nchars = -1;
+      itb.string.s = NULL;
+      itb.string.lstring = Qnil;
       itb.frame_window_p = FRAME_WINDOW_P (SELECTED_FRAME ()); /* guesswork */
       itb.first_elt = 1;
       itb.separator_limit = -1;
@@ -18248,6 +18921,11 @@
     }
 #endif /* not USE_X_TOOLKIT */
 
+  /* FIXME: This should be controlled by a user option.  See the
+     comments in redisplay_tool_bar and display_mode_line about
+     this.  */
+  it.paragraph_embedding = L2R;
+
   if (! mode_line_inverse_video)
     /* Force the menu-bar to be displayed in the default face.  */
     it.base_face_id = it.face_id = DEFAULT_FACE_ID;
@@ -18425,6 +19103,11 @@
     /* Force the mode-line to be displayed in the default face.  */
     it.base_face_id = it.face_id = DEFAULT_FACE_ID;
 
+  /* FIXME: This should be controlled by a user option.  But
+     supporting such an option is not trivial, since the mode line is
+     made up of many separate strings.  */
+  it.paragraph_embedding = L2R;
+
   record_unwind_protect (unwind_format_mode_line,
                         format_mode_line_unwind_data (NULL, Qnil, 0));
 
@@ -19970,6 +20653,7 @@
   int hpos_at_start = it->hpos;
   int saved_face_id = it->face_id;
   struct glyph_row *row = it->glyph_row;
+  EMACS_INT it_charpos;
 
   /* Initialize the iterator IT for iteration over STRING beginning
      with index START.  */
@@ -19978,10 +20662,10 @@
   if (string && STRINGP (lisp_string))
     /* LISP_STRING is the one returned by decode_mode_spec.  We should
        ignore its text properties.  */
-    it->stop_charpos = -1;
+    it->stop_charpos = it->end_charpos;
 
-  /* If displaying STRING, set up the face of the iterator
-     from LISP_STRING, if that's given.  */
+  /* If displaying STRING, set up the face of the iterator from
+     FACE_STRING, if that's given.  */
   if (STRINGP (face_string))
     {
       EMACS_INT endptr;
@@ -20015,6 +20699,11 @@
   row->phys_height = it->max_phys_ascent + it->max_phys_descent;
   row->extra_line_spacing = it->max_extra_line_spacing;
 
+  if (STRINGP (it->string))
+    it_charpos = IT_STRING_CHARPOS (*it);
+  else
+    it_charpos = IT_CHARPOS (*it);
+
   /* This condition is for the case that we are called with current_x
      past last_visible_x.  */
   while (it->current_x < max_x)
@@ -20027,10 +20716,10 @@
 
       /* Produce glyphs.  */
       x_before = it->current_x;
-      n_glyphs_before = it->glyph_row->used[TEXT_AREA];
+      n_glyphs_before = row->used[TEXT_AREA];
       PRODUCE_GLYPHS (it);
 
-      nglyphs = it->glyph_row->used[TEXT_AREA] - n_glyphs_before;
+      nglyphs = row->used[TEXT_AREA] - n_glyphs_before;
       i = 0;
       x = x_before;
       while (i < nglyphs)
@@ -20044,12 +20733,18 @@
              if (CHAR_GLYPH_PADDING_P (*glyph))
                {
                  /* A wide character is unbreakable.  */
-                 it->glyph_row->used[TEXT_AREA] = n_glyphs_before;
+                 if (row->reversed_p)
+                   unproduce_glyphs (it, row->used[TEXT_AREA]
+                                     - n_glyphs_before);
+                 row->used[TEXT_AREA] = n_glyphs_before;
                  it->current_x = x_before;
                }
              else
                {
-                 it->glyph_row->used[TEXT_AREA] = n_glyphs_before + i;
+                 if (row->reversed_p)
+                   unproduce_glyphs (it, row->used[TEXT_AREA]
+                                     - (n_glyphs_before + i));
+                 row->used[TEXT_AREA] = n_glyphs_before + i;
                  it->current_x = x;
                }
              break;
@@ -20059,7 +20754,7 @@
              /* Glyph is at least partially visible.  */
              ++it->hpos;
              if (x < it->first_visible_x)
-               it->glyph_row->x = x - it->first_visible_x;
+               row->x = x - it->first_visible_x;
            }
          else
            {
@@ -20091,6 +20786,10 @@
        }
 
       set_iterator_to_next (it, 1);
+      if (STRINGP (it->string))
+       it_charpos = IT_STRING_CHARPOS (*it);
+      else
+       it_charpos = IT_CHARPOS (*it);
 
       /* Stop if truncating at the right edge.  */
       if (it->line_wrap == TRUNCATE
@@ -20098,7 +20797,7 @@
        {
          /* Add truncation mark, but don't do it if the line is
             truncated at a padding space.  */
-         if (IT_CHARPOS (*it) < it->string_nchars)
+         if (it_charpos < it->string_nchars)
            {
              if (!FRAME_WINDOW_P (it->f))
                {
@@ -20106,9 +20805,20 @@
 
                  if (it->current_x > it->last_visible_x)
                    {
-                     for (ii = row->used[TEXT_AREA] - 1; ii > 0; --ii)
-                       if (!CHAR_GLYPH_PADDING_P (row->glyphs[TEXT_AREA][ii]))
-                         break;
+                     if (!row->reversed_p)
+                       {
+                         for (ii = row->used[TEXT_AREA] - 1; ii > 0; --ii)
+                           if (!CHAR_GLYPH_PADDING_P 
(row->glyphs[TEXT_AREA][ii]))
+                             break;
+                       }
+                     else
+                       {
+                         for (ii = 0; ii < row->used[TEXT_AREA]; ii++)
+                           if (!CHAR_GLYPH_PADDING_P 
(row->glyphs[TEXT_AREA][ii]))
+                             break;
+                         unproduce_glyphs (it, ii + 1);
+                         ii = row->used[TEXT_AREA] - (ii + 1);
+                       }
                      for (n = row->used[TEXT_AREA]; ii < n; ++ii)
                        {
                          row->used[TEXT_AREA] = ii;
@@ -20117,7 +20827,7 @@
                    }
                  produce_special_glyphs (it, IT_TRUNCATION);
                }
-             it->glyph_row->truncated_on_right_p = 1;
+             row->truncated_on_right_p = 1;
            }
          break;
        }
@@ -20125,11 +20835,11 @@
 
   /* Maybe insert a truncation at the left.  */
   if (it->first_visible_x
-      && IT_CHARPOS (*it) > 0)
+      && it_charpos > 0)
     {
       if (!FRAME_WINDOW_P (it->f))
        insert_left_trunc_glyphs (it);
-      it->glyph_row->truncated_on_left_p = 1;
+      row->truncated_on_left_p = 1;
     }
 
   it->face_id = saved_face_id;


reply via email to

[Prev in Thread] Current Thread [Next in Thread]