Replace pEd with more-general pI, and fix some printf arg casts.
[bpt/emacs.git] / src / bidi.c
index 46382b2..88c45e2 100644 (file)
@@ -1,53 +1,57 @@
 /* Low-level bidirectional buffer-scanning functions for GNU Emacs.
-   Copyright (C) 2000, 2001, 2004, 2005        Free Software Foundation, Inc.
+   Copyright (C) 2000-2001, 2004-2005, 2009-2011
+   Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
-
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Eli Zaretskii <eliz@gnu.org>.
 
-/* A sequential implementation of the Unicode Bidirectional algorithm,
+   A sequential implementation of the Unicode Bidirectional algorithm,
    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.
+   designed to be called once for every character in the buffer or
+   string.
 
-   The main entry point is bidi_get_next_char_visually.  Each time it
+   The main entry point is bidi_move_to_visually_next.  Each time it
    is called, it finds the next character in the visual order, and
    returns its information in a special structure.  The caller is then
    expected to process this character for display or any other
-   purposes, and call bidi_get_next_char_visually for the next
-   character.  See the comments in bidi_get_next_char_visually for
-   more details about its algorithm that finds the next visual-order
+   purposes, and call bidi_move_to_visually_next for the next
+   character.  See the comments in bidi_move_to_visually_next for more
+   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
+   bidi_mirror_char.  The first determines the base direction of a
+   paragraph, while the second returns the mirrored version of its
+   argument character.
+
+   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.
+
    A note about references to UAX#9 rules: if the reference says
    something like "X9/Retaining", it means that you need to refer to
    rule X9 and to its modifications decribed in the "Implementation
    Notes" section of UAX#9, under "Retaining Format Codes".  */
 
-#ifdef HAVE_CONFIG_H
 #include <config.h>
-#endif
-
 #include <stdio.h>
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
+#include <setjmp.h>
 
 #include "lisp.h"
 #include "buffer.h"
@@ -56,8 +60,9 @@ Boston, MA 02110-1301, USA.  */
 
 static int bidi_initialized = 0;
 
-static Lisp_Object bidi_type_table;
+static Lisp_Object bidi_type_table, bidi_mirror_table;
 
+/* FIXME: Remove these when bidi_explicit_dir_char uses a lookup table.  */
 #define LRM_CHAR   0x200E
 #define RLM_CHAR   0x200F
 #define LRE_CHAR   0x202A
@@ -66,81 +71,16 @@ static Lisp_Object bidi_type_table;
 #define LRO_CHAR   0x202D
 #define RLO_CHAR   0x202E
 
-#define CHARSET_HEBREW   0x88
-#define CHARSET_ARABIC  0x87
-#define CHARSET_SYRIAC  -1     /* these are undefined yet, -1 is invalid */
-#define CHARSET_THAANA  -1
-
-/* FIXME: need to define wrappers for FETCH_CHAR etc. that return
-   BIDI_EOB when they hit ZV.  */
 #define BIDI_EOB   -1
-#define BIDI_BOB   -2
-
-#ifdef TEST_STANDALONE
-/* Testing.  */
-
-static unsigned char *input_buf;
-static size_t input_buf_size;
-
-int _fetch_multibyte_char_len, _c_c_;
-
-#undef FETCH_CHAR_ADVANCE
-#define FETCH_CHAR_ADVANCE(ch, cp, bp) \
-  do {                                \
-    ch = input_buf[cp];                       \
-    (cp)++;                           \
-    (bp)++;                           \
-    if (ch == '\0')                   \
-      ch = BIDI_EOB;                          \
-  } while (0)
-
-#undef FETCH_CHAR
-#define FETCH_CHAR(n)          ((_c_c_ = input_buf[n]) ? _c_c_ : BIDI_EOB)
-
-#undef CHAR_CHARSET
-#define CHAR_CHARSET(c)                                         \
-  (((c) >= 128 || ((c) < 8 && (c)) || ((c) >= 'A' && (c) < 'X')) \
-   ? CHARSET_HEBREW                                             \
-   : ((((c) >= 'X' && (c) <= 'Z') || ((c) >= '6' && (c) <= '9')) \
-      ? CHARSET_ARABIC                                          \
-      : CHARSET_ASCII))
-
-#undef CHAR_TO_BYTE
-#define CHAR_TO_BYTE(pos) (pos)
-
-#define char_bytes(ch) 1
-
-#undef LRE_CHAR
-#undef LRO_CHAR
-#undef RLE_CHAR
-#undef RLO_CHAR
-#undef PDF_CHAR
-#undef RLM_CHAR
-#undef LRM_CHAR
-
-#define LRE_CHAR   1
-#define LRO_CHAR   2
-#define RLE_CHAR   3
-#define RLO_CHAR   4
-#define PDF_CHAR   5
-#define RLM_CHAR   6
-#define LRM_CHAR   7
-
-static const char *bidi_name[] =
-  {
-    "[???]", "[LRE]", "[LRO]", "[RLE]", "[RLO]", "[PDF]", "[RLM]", "[LRM]"
-  };
-
-#endif /* TEST_STANDALONE */
 
 /* Local data structures.  (Look in dispextern.h for the rest.)  */
 
 /* What we need to know about the current paragraph.  */
 struct bidi_paragraph_info {
-  int start_bytepos;   /* byte position where it begins */
-  int end_bytepos;     /* byte position where it ends */
-  int embedding_level; /* its basic embedding level */
-  bidi_dir_t base_dir; /* its base direction */
+  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.  */
@@ -151,329 +91,104 @@ typedef enum {
   STRONG
 } bidi_category_t;
 
+extern int bidi_ignore_explicit_marks_for_paragraph_level EXTERNALLY_VISIBLE;
 int bidi_ignore_explicit_marks_for_paragraph_level = 1;
 
-bidi_dir_t bidi_overriding_paragraph_direction = NEUTRAL_DIR;
-
-/* FIXME: Unused? */
-#define ASCII_BIDI_TYPE_SET(STR, TYPE)                 \
-  do {                                                 \
-    unsigned char *p;                                  \
-    for (p = (STR); *p; p++)                           \
-      CHAR_TABLE_SET (bidi_type_table, *p, (TYPE));    \
-  } while (0)
+static Lisp_Object paragraph_start_re, paragraph_separate_re;
+static Lisp_Object Qparagraph_start, Qparagraph_separate;
 
 static void
-bidi_initialize ()
+bidi_initialize (void)
 {
-  /* FIXME: This should come from the Unicode Database.  */
-  struct {
-    int from, to;
-    bidi_type_t type;
-  } bidi_type[] =
-      { { 0x0000, 0x0008, WEAK_BN },
-       { 0x0009, 0x0000, NEUTRAL_S },
-       { 0x000A, 0x0000, NEUTRAL_B },
-       { 0x000B, 0x0000, NEUTRAL_S },
-       { 0x000C, 0x0000, NEUTRAL_WS },
-       { 0x000D, 0x0000, NEUTRAL_B },
-       { 0x000E, 0x001B, WEAK_BN },
-       { 0x001C, 0x001E, NEUTRAL_B },
-       { 0x001F, 0x0000, NEUTRAL_S },
-       { 0x0020, 0x0000, NEUTRAL_WS },
-       { 0x0021, 0x0022, NEUTRAL_ON },
-       { 0x0023, 0x0025, WEAK_ET },
-       { 0x0026, 0x002A, NEUTRAL_ON },
-       { 0x002B, 0x0000, WEAK_ET },
-       { 0x002C, 0x0000, WEAK_CS },
-       { 0x002D, 0x0000, WEAK_ET },
-       { 0x002E, 0x0000, WEAK_CS },
-       { 0x002F, 0x0000, WEAK_ES },
-       { 0x0030, 0x0039, WEAK_EN },
-       { 0x003A, 0x0000, WEAK_CS },
-       { 0x003B, 0x0040, NEUTRAL_ON },
-       { 0x005B, 0x0060, NEUTRAL_ON },
-       { 0x007B, 0x007E, NEUTRAL_ON },
-       { 0x007F, 0x0084, WEAK_BN },
-       { 0x0085, 0x0000, NEUTRAL_B },
-       { 0x0086, 0x009F, WEAK_BN },
-       { 0x00A0, 0x0000, WEAK_CS },
-       { 0x00A1, 0x0000, NEUTRAL_ON },
-       { 0x00A2, 0x00A5, WEAK_ET },
-       { 0x00A6, 0x00A9, NEUTRAL_ON },
-       { 0x00AB, 0x00AF, NEUTRAL_ON },
-       { 0x00B0, 0x00B1, WEAK_ET },
-       { 0x00B2, 0x00B3, WEAK_EN },
-       { 0x00B4, 0x0000, NEUTRAL_ON },
-       { 0x00B6, 0x00B8, NEUTRAL_ON },
-       { 0x00B9, 0x0000, WEAK_EN },
-       { 0x00BB, 0x00BF, NEUTRAL_ON },
-       { 0x00D7, 0x0000, NEUTRAL_ON },
-       { 0x00F7, 0x0000, NEUTRAL_ON },
-       { 0x02B9, 0x02BA, NEUTRAL_ON },
-       { 0x02C2, 0x02CF, NEUTRAL_ON },
-       { 0x02D2, 0x02DF, NEUTRAL_ON },
-       { 0x02E5, 0x02ED, NEUTRAL_ON },
-       { 0x0300, 0x036F, WEAK_NSM },
-       { 0x0374, 0x0375, NEUTRAL_ON },
-       { 0x037E, 0x0385, NEUTRAL_ON },
-       { 0x0387, 0x0000, NEUTRAL_ON },
-       { 0x03F6, 0x0000, NEUTRAL_ON },
-       { 0x0483, 0x0489, WEAK_NSM },
-       { 0x058A, 0x0000, NEUTRAL_ON },
-       { 0x0591, 0x05BD, WEAK_NSM },
-       { 0x05BE, 0x0000, STRONG_R },
-       { 0x05BF, 0x0000, WEAK_NSM },
-       { 0x05C0, 0x0000, STRONG_R },
-       { 0x05C1, 0x05C2, WEAK_NSM },
-       { 0x05C3, 0x0000, STRONG_R },
-       { 0x05C4, 0x0000, WEAK_NSM },
-       { 0x05D0, 0x05F4, STRONG_R },
-       { 0x060C, 0x0000, WEAK_CS },
-       { 0x061B, 0x064A, STRONG_AL },
-       { 0x064B, 0x0655, WEAK_NSM },
-       { 0x0660, 0x0669, WEAK_AN },
-       { 0x066A, 0x0000, WEAK_ET },
-       { 0x066B, 0x066C, WEAK_AN },
-       { 0x066D, 0x066F, STRONG_AL },
-       { 0x0670, 0x0000, WEAK_NSM },
-       { 0x0671, 0x06D5, STRONG_AL },
-       { 0x06D6, 0x06DC, WEAK_NSM },
-       { 0x06DD, 0x0000, STRONG_AL },
-       { 0x06DE, 0x06E4, WEAK_NSM },
-       { 0x06E5, 0x06E6, STRONG_AL },
-       { 0x06E7, 0x06E8, WEAK_NSM },
-       { 0x06E9, 0x0000, NEUTRAL_ON },
-       { 0x06EA, 0x06ED, WEAK_NSM },
-       { 0x06F0, 0x06F9, WEAK_EN },
-       { 0x06FA, 0x070D, STRONG_AL },
-       { 0x070F, 0x0000, WEAK_BN },
-       { 0x0710, 0x0000, STRONG_AL },
-       { 0x0711, 0x0000, WEAK_NSM },
-       { 0x0712, 0x072C, STRONG_AL },
-       { 0x0730, 0x074A, WEAK_NSM },
-       { 0x0780, 0x07A5, STRONG_AL },
-       { 0x07A6, 0x07B0, WEAK_NSM },
-       { 0x07B1, 0x0000, STRONG_AL },
-       { 0x0901, 0x0902, WEAK_NSM },
-       { 0x093C, 0x0000, WEAK_NSM },
-       { 0x0941, 0x0948, WEAK_NSM },
-       { 0x094D, 0x0000, WEAK_NSM },
-       { 0x0951, 0x0954, WEAK_NSM },
-       { 0x0962, 0x0963, WEAK_NSM },
-       { 0x0981, 0x0000, WEAK_NSM },
-       { 0x09BC, 0x0000, WEAK_NSM },
-       { 0x09C1, 0x09C4, WEAK_NSM },
-       { 0x09CD, 0x0000, WEAK_NSM },
-       { 0x09E2, 0x09E3, WEAK_NSM },
-       { 0x09F2, 0x09F3, WEAK_ET },
-       { 0x0A02, 0x0000, WEAK_NSM },
-       { 0x0A3C, 0x0000, WEAK_NSM },
-       { 0x0A41, 0x0A4D, WEAK_NSM },
-       { 0x0A70, 0x0A71, WEAK_NSM },
-       { 0x0A81, 0x0A82, WEAK_NSM },
-       { 0x0ABC, 0x0000, WEAK_NSM },
-       { 0x0AC1, 0x0AC8, WEAK_NSM },
-       { 0x0ACD, 0x0000, WEAK_NSM },
-       { 0x0B01, 0x0000, WEAK_NSM },
-       { 0x0B3C, 0x0000, WEAK_NSM },
-       { 0x0B3F, 0x0000, WEAK_NSM },
-       { 0x0B41, 0x0B43, WEAK_NSM },
-       { 0x0B4D, 0x0B56, WEAK_NSM },
-       { 0x0B82, 0x0000, WEAK_NSM },
-       { 0x0BC0, 0x0000, WEAK_NSM },
-       { 0x0BCD, 0x0000, WEAK_NSM },
-       { 0x0C3E, 0x0C40, WEAK_NSM },
-       { 0x0C46, 0x0C56, WEAK_NSM },
-       { 0x0CBF, 0x0000, WEAK_NSM },
-       { 0x0CC6, 0x0000, WEAK_NSM },
-       { 0x0CCC, 0x0CCD, WEAK_NSM },
-       { 0x0D41, 0x0D43, WEAK_NSM },
-       { 0x0D4D, 0x0000, WEAK_NSM },
-       { 0x0DCA, 0x0000, WEAK_NSM },
-       { 0x0DD2, 0x0DD6, WEAK_NSM },
-       { 0x0E31, 0x0000, WEAK_NSM },
-       { 0x0E34, 0x0E3A, WEAK_NSM },
-       { 0x0E3F, 0x0000, WEAK_ET },
-       { 0x0E47, 0x0E4E, WEAK_NSM },
-       { 0x0EB1, 0x0000, WEAK_NSM },
-       { 0x0EB4, 0x0EBC, WEAK_NSM },
-       { 0x0EC8, 0x0ECD, WEAK_NSM },
-       { 0x0F18, 0x0F19, WEAK_NSM },
-       { 0x0F35, 0x0000, WEAK_NSM },
-       { 0x0F37, 0x0000, WEAK_NSM },
-       { 0x0F39, 0x0000, WEAK_NSM },
-       { 0x0F3A, 0x0F3D, NEUTRAL_ON },
-       { 0x0F71, 0x0F7E, WEAK_NSM },
-       { 0x0F80, 0x0F84, WEAK_NSM },
-       { 0x0F86, 0x0F87, WEAK_NSM },
-       { 0x0F90, 0x0FBC, WEAK_NSM },
-       { 0x0FC6, 0x0000, WEAK_NSM },
-       { 0x102D, 0x1030, WEAK_NSM },
-       { 0x1032, 0x1037, WEAK_NSM },
-       { 0x1039, 0x0000, WEAK_NSM },
-       { 0x1058, 0x1059, WEAK_NSM },
-       { 0x1680, 0x0000, NEUTRAL_WS },
-       { 0x169B, 0x169C, NEUTRAL_ON },
-       { 0x1712, 0x1714, WEAK_NSM },
-       { 0x1732, 0x1734, WEAK_NSM },
-       { 0x1752, 0x1753, WEAK_NSM },
-       { 0x1772, 0x1773, WEAK_NSM },
-       { 0x17B7, 0x17BD, WEAK_NSM },
-       { 0x17C6, 0x0000, WEAK_NSM },
-       { 0x17C9, 0x17D3, WEAK_NSM },
-       { 0x17DB, 0x0000, WEAK_ET },
-       { 0x1800, 0x180A, NEUTRAL_ON },
-       { 0x180B, 0x180D, WEAK_NSM },
-       { 0x180E, 0x0000, WEAK_BN },
-       { 0x18A9, 0x0000, WEAK_NSM },
-       { 0x1FBD, 0x0000, NEUTRAL_ON },
-       { 0x1FBF, 0x1FC1, NEUTRAL_ON },
-       { 0x1FCD, 0x1FCF, NEUTRAL_ON },
-       { 0x1FDD, 0x1FDF, NEUTRAL_ON },
-       { 0x1FED, 0x1FEF, NEUTRAL_ON },
-       { 0x1FFD, 0x1FFE, NEUTRAL_ON },
-       { 0x2000, 0x200A, NEUTRAL_WS },
-       { 0x200B, 0x200D, WEAK_BN },
-       { 0x200F, 0x0000, STRONG_R },
-       { 0x2010, 0x2027, NEUTRAL_ON },
-       { 0x2028, 0x0000, NEUTRAL_WS },
-       { 0x2029, 0x0000, NEUTRAL_B },
-       { 0x202A, 0x0000, LRE },
-       { 0x202B, 0x0000, RLE },
-       { 0x202C, 0x0000, PDF },
-       { 0x202D, 0x0000, LRO },
-       { 0x202E, 0x0000, RLO },
-       { 0x202F, 0x0000, NEUTRAL_WS },
-       { 0x2030, 0x2034, WEAK_ET },
-       { 0x2035, 0x2057, NEUTRAL_ON },
-       { 0x205F, 0x0000, NEUTRAL_WS },
-       { 0x2060, 0x206F, WEAK_BN },
-       { 0x2070, 0x0000, WEAK_EN },
-       { 0x2074, 0x2079, WEAK_EN },
-       { 0x207A, 0x207B, WEAK_ET },
-       { 0x207C, 0x207E, NEUTRAL_ON },
-       { 0x2080, 0x2089, WEAK_EN },
-       { 0x208A, 0x208B, WEAK_ET },
-       { 0x208C, 0x208E, NEUTRAL_ON },
-       { 0x20A0, 0x20B1, WEAK_ET },
-       { 0x20D0, 0x20EA, WEAK_NSM },
-       { 0x2100, 0x2101, NEUTRAL_ON },
-       { 0x2103, 0x2106, NEUTRAL_ON },
-       { 0x2108, 0x2109, NEUTRAL_ON },
-       { 0x2114, 0x0000, NEUTRAL_ON },
-       { 0x2116, 0x2118, NEUTRAL_ON },
-       { 0x211E, 0x2123, NEUTRAL_ON },
-       { 0x2125, 0x0000, NEUTRAL_ON },
-       { 0x2127, 0x0000, NEUTRAL_ON },
-       { 0x2129, 0x0000, NEUTRAL_ON },
-       { 0x212E, 0x0000, WEAK_ET },
-       { 0x2132, 0x0000, NEUTRAL_ON },
-       { 0x213A, 0x0000, NEUTRAL_ON },
-       { 0x2140, 0x2144, NEUTRAL_ON },
-       { 0x214A, 0x215F, NEUTRAL_ON },
-       { 0x2190, 0x2211, NEUTRAL_ON },
-       { 0x2212, 0x2213, WEAK_ET },
-       { 0x2214, 0x2335, NEUTRAL_ON },
-       { 0x237B, 0x2394, NEUTRAL_ON },
-       { 0x2396, 0x244A, NEUTRAL_ON },
-       { 0x2460, 0x249B, WEAK_EN },
-       { 0x24EA, 0x0000, WEAK_EN },
-       { 0x24EB, 0x2FFB, NEUTRAL_ON },
-       { 0x3000, 0x0000, NEUTRAL_WS },
-       { 0x3001, 0x3004, NEUTRAL_ON },
-       { 0x3008, 0x3020, NEUTRAL_ON },
-       { 0x302A, 0x302F, WEAK_NSM },
-       { 0x3030, 0x0000, NEUTRAL_ON },
-       { 0x3036, 0x3037, NEUTRAL_ON },
-       { 0x303D, 0x303F, NEUTRAL_ON },
-       { 0x3099, 0x309A, WEAK_NSM },
-       { 0x309B, 0x309C, NEUTRAL_ON },
-       { 0x30A0, 0x0000, NEUTRAL_ON },
-       { 0x30FB, 0x0000, NEUTRAL_ON },
-       { 0x3251, 0x325F, NEUTRAL_ON },
-       { 0x32B1, 0x32BF, NEUTRAL_ON },
-       { 0xA490, 0xA4C6, NEUTRAL_ON },
-       { 0xFB1D, 0x0000, STRONG_R },
-       { 0xFB1E, 0x0000, WEAK_NSM },
-       { 0xFB1F, 0xFB28, STRONG_R },
-       { 0xFB29, 0x0000, WEAK_ET },
-       { 0xFB2A, 0xFB4F, STRONG_R },
-       { 0xFB50, 0xFD3D, STRONG_AL },
-       { 0xFD3E, 0xFD3F, NEUTRAL_ON },
-       { 0xFD50, 0xFDFC, STRONG_AL },
-       { 0xFE00, 0xFE23, WEAK_NSM },
-       { 0xFE30, 0xFE4F, NEUTRAL_ON },
-       { 0xFE50, 0x0000, WEAK_CS },
-       { 0xFE51, 0x0000, NEUTRAL_ON },
-       { 0xFE52, 0x0000, WEAK_CS },
-       { 0xFE54, 0x0000, NEUTRAL_ON },
-       { 0xFE55, 0x0000, WEAK_CS },
-       { 0xFE56, 0xFE5E, NEUTRAL_ON },
-       { 0xFE5F, 0x0000, WEAK_ET },
-       { 0xFE60, 0xFE61, NEUTRAL_ON },
-       { 0xFE62, 0xFE63, WEAK_ET },
-       { 0xFE64, 0xFE68, NEUTRAL_ON },
-       { 0xFE69, 0xFE6A, WEAK_ET },
-       { 0xFE6B, 0x0000, NEUTRAL_ON },
-       { 0xFE70, 0xFEFC, STRONG_AL },
-       { 0xFEFF, 0x0000, WEAK_BN },
-       { 0xFF01, 0xFF02, NEUTRAL_ON },
-       { 0xFF03, 0xFF05, WEAK_ET },
-       { 0xFF06, 0xFF0A, NEUTRAL_ON },
-       { 0xFF0B, 0x0000, WEAK_ET },
-       { 0xFF0C, 0x0000, WEAK_CS },
-       { 0xFF0D, 0x0000, WEAK_ET },
-       { 0xFF0E, 0x0000, WEAK_CS },
-       { 0xFF0F, 0x0000, WEAK_ES },
-       { 0xFF10, 0xFF19, WEAK_EN },
-       { 0xFF1A, 0x0000, WEAK_CS },
-       { 0xFF1B, 0xFF20, NEUTRAL_ON },
-       { 0xFF3B, 0xFF40, NEUTRAL_ON },
-       { 0xFF5B, 0xFF65, NEUTRAL_ON },
-       { 0xFFE0, 0xFFE1, WEAK_ET },
-       { 0xFFE2, 0xFFE4, NEUTRAL_ON },
-       { 0xFFE5, 0xFFE6, WEAK_ET },
-       { 0xFFE8, 0xFFEE, NEUTRAL_ON },
-       { 0xFFF9, 0xFFFB, WEAK_BN },
-       { 0xFFFC, 0xFFFD, NEUTRAL_ON },
-       { 0x1D167, 0x1D169, WEAK_NSM },
-       { 0x1D173, 0x1D17A, WEAK_BN },
-       { 0x1D17B, 0x1D182, WEAK_NSM },
-       { 0x1D185, 0x1D18B, WEAK_NSM },
-       { 0x1D1AA, 0x1D1AD, WEAK_NSM },
-       { 0x1D7CE, 0x1D7FF, WEAK_EN },
-       { 0xE0001, 0xE007F, WEAK_BN } };
+
+#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 ? bidi_type[i].to : bidi_type[i].from,
+    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;
 }
 
-static int
-bidi_is_arabic_number (int ch)
+/* Return the bidi type of a character CH, subject to the current
+   directional OVERRIDE.  */
+static INLINE bidi_type_t
+bidi_get_type (int ch, bidi_dir_t override)
 {
-#ifdef TEST_STANDALONE
-  return ch >= '6' && ch <= '9';
-#else
-  return 0;    /* FIXME! */
-#endif
+  bidi_type_t default_type;
+
+  if (ch == BIDI_EOB)
+    return NEUTRAL_B;
+  if (ch < 0 || ch > MAX_CHAR)
+    abort ();
+
+  default_type = (bidi_type_t) XINT (CHAR_TABLE_REF (bidi_type_table, ch));
+
+  if (override == NEUTRAL_DIR)
+    return default_type;
+
+  switch (default_type)
+    {
+      /* Although UAX#9 does not tell, it doesn't make sense to
+        override NEUTRAL_B and LRM/RLM characters.  */
+      case NEUTRAL_B:
+      case LRE:
+      case LRO:
+      case RLE:
+      case RLO:
+      case PDF:
+       return default_type;
+      default:
+       switch (ch)
+         {
+           case LRM_CHAR:
+           case RLM_CHAR:
+             return default_type;
+           default:
+             if (override == L2R) /* X6 */
+               return STRONG_L;
+             else if (override == R2L)
+               return STRONG_R;
+             else
+               abort ();       /* can't happen: handled above */
+         }
+    }
 }
 
-/* Return the bidi type of a character CH.  */
-bidi_type_t
-bidi_get_type (int ch)
+static void
+bidi_check_type (bidi_type_t type)
 {
-  return (bidi_type_t) XINT (CHAR_TABLE_REF (bidi_type_table, ch));
+  if (type < UNKNOWN_BT || type > NEUTRAL_ON)
+    abort ();
 }
 
 /* Given a bidi TYPE of a character, return its category.  */
-bidi_category_t
+static INLINE bidi_category_t
 bidi_get_category (bidi_type_t type)
 {
   switch (type)
@@ -507,35 +222,43 @@ bidi_get_category (bidi_type_t type)
     }
 }
 
-/* FIXME: exceedingly temporary!  Should consult the Unicode database
-   of character properties.  */
+/* Return the mirrored character of C, if it has one.  If C has no
+   mirrored counterpart, return C.
+   Note: The conditions in UAX#9 clause L4 regarding the surrounding
+   context must be tested by the caller.  */
 int
 bidi_mirror_char (int c)
 {
-  static const char mirrored_pairs[] = "()<>[]{}";
-  const char *p = strchr (mirrored_pairs, c);
+  Lisp_Object val;
+
+  if (c == BIDI_EOB)
+    return c;
+  if (c < 0 || c > MAX_CHAR)
+    abort ();
 
-  if (p)
+  val = CHAR_TABLE_REF (bidi_mirror_table, c);
+  if (INTEGERP (val))
     {
-      size_t i = p - mirrored_pairs;
+      int v = XINT (val);
 
-      if ((i & 1) == 0)
-       return mirrored_pairs[i + 1];
-      else
-       return mirrored_pairs[i - 1];
+      if (v < 0 || v > MAX_CHAR)
+       abort ();
+
+      return v;
     }
+
   return c;
 }
 
 /* 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
+static INLINE void
 bidi_copy_it (struct bidi_it *to, struct bidi_it *from)
 {
   int i;
 
-  /* Copy everything except the level stack.  */
-  memcpy (to, from, ((int)&((struct bidi_it *)0)->level_stack[0]));
+  /* Copy everything except the level stack and beyond.  */
+  memcpy (to, from, ((size_t)&((struct bidi_it *)0)->level_stack[0]));
 
   /* Copy the active part of the level stack.  */
   to->level_stack[0] = from->level_stack[0]; /* level zero is always in use */
@@ -545,18 +268,33 @@ bidi_copy_it (struct bidi_it *to, struct bidi_it *from)
 
 /* Caching the bidi iterator states.  */
 
-static struct bidi_it bidi_cache[1000]; /* FIXME: make this dynamically allocated! */
-static int bidi_cache_idx;
-static int bidi_cache_last_idx;
+#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 inline void
+static INLINE void
 bidi_cache_reset (void)
 {
   bidi_cache_idx = 0;
   bidi_cache_last_idx = -1;
 }
 
-static inline void
+static INLINE void
+bidi_cache_shrink (void)
+{
+  if (bidi_cache_size > BIDI_CACHE_CHUNK)
+    {
+      bidi_cache_size = BIDI_CACHE_CHUNK;
+      bidi_cache =
+       (struct bidi_it *) xrealloc (bidi_cache, bidi_cache_size * elsz);
+    }
+  bidi_cache_reset ();
+}
+
+static INLINE void
 bidi_cache_fetch_state (int idx, struct bidi_it *bidi_it)
 {
   int current_scan_dir = bidi_it->scan_dir;
@@ -573,8 +311,8 @@ bidi_cache_fetch_state (int idx, struct bidi_it *bidi_it)
    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
-bidi_cache_search (int charpos, int level, int dir)
+static INLINE int
+bidi_cache_search (EMACS_INT charpos, int level, int dir)
 {
   int i, i_start;
 
@@ -664,7 +402,7 @@ bidi_cache_find_level_change (int level, int dir, int before)
   return -1;
 }
 
-static inline void
+static INLINE void
 bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
 {
   int idx;
@@ -677,8 +415,23 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
   if (idx < 0)
     {
       idx = bidi_cache_idx;
-      if (idx > sizeof (bidi_cache) / sizeof (bidi_cache[0]) - 1)
-       abort ();
+      /* 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);
+       }
+      /* 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 &&
+         (bidi_it->charpos > bidi_cache[idx - 1].charpos + 1
+          || bidi_it->charpos < bidi_cache[0].charpos))
+       {
+         bidi_cache_reset ();
+         idx = 0;
+       }
       bidi_copy_it (&bidi_cache[idx], bidi_it);
       if (!resolved)
        bidi_cache[idx].resolved_level = -1;
@@ -688,7 +441,9 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
       /* Copy only the members which could have changed, to avoid
         costly copying of the entire struct.  */
       bidi_cache[idx].type = bidi_it->type;
-      bidi_cache[idx].orig_type = bidi_it->orig_type;
+      bidi_check_type (bidi_it->type);
+      bidi_cache[idx].type_after_w1 = bidi_it->type_after_w1;
+      bidi_check_type (bidi_it->type_after_w1);
       if (resolved)
        bidi_cache[idx].resolved_level = bidi_it->resolved_level;
       else
@@ -705,8 +460,8 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
     bidi_cache_idx = idx + 1;
 }
 
-static inline bidi_type_t
-bidi_cache_find (int charpos, int level, struct bidi_it *bidi_it)
+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);
 
@@ -714,7 +469,7 @@ bidi_cache_find (int charpos, int level, struct bidi_it *bidi_it)
     {
       bidi_dir_t current_scan_dir = bidi_it->scan_dir;
 
-      *bidi_it = bidi_cache[i];
+      bidi_copy_it (bidi_it, &bidi_cache[i]);
       bidi_cache_last_idx = i;
       /* Don't let scan direction from from the cached state override
         the current scan direction.  */
@@ -725,7 +480,7 @@ bidi_cache_find (int charpos, int level, struct bidi_it *bidi_it)
   return UNKNOWN_BT;
 }
 
-static inline int
+static INLINE int
 bidi_peek_at_next_level (struct bidi_it *bidi_it)
 {
   if (bidi_cache_idx == 0 || bidi_cache_last_idx == -1)
@@ -733,24 +488,38 @@ bidi_peek_at_next_level (struct bidi_it *bidi_it)
   return bidi_cache[bidi_cache_last_idx + bidi_it->scan_dir].resolved_level;
 }
 
-/* Return non-zero if buffer's byte position POS is the last character
-   of a paragraph.  THIS_CH is the character preceding the one at POS in
-   the buffer.  */
-int
-bidi_at_paragraph_end (int this_ch, int pos)
+/* 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)
 {
-  int next_ch = FETCH_CHAR (pos);
+  Lisp_Object sep_re;
+  Lisp_Object start_re;
+  EMACS_INT val;
 
-  /* FIXME: This should support all Unicode characters that can end a
-     paragraph.  */
-  return (this_ch == '\n' && next_ch == '\n') || this_ch == BIDI_EOB;
+  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.  */
-static inline void
+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;
@@ -762,138 +531,248 @@ bidi_set_sor_type (struct bidi_it *bidi_it, int level_before, int level_after)
      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 (level_before < level_after || !bidi_it->prev_was_pdf)
+  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.orig_type =
-    bidi_it->last_strong.pristine_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.orig_type =
-    bidi_it->next_for_neutral.pristine_type = UNKNOWN_BT;
+  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 */
 }
 
+static void
+bidi_line_init (struct bidi_it *bidi_it)
+{
+  bidi_it->scan_dir = 1; /* FIXME: do we need to have control on this? */
+  bidi_it->resolved_level = bidi_it->level_stack[0].level;
+  bidi_it->level_stack[0].override = NEUTRAL_DIR; /* X1 */
+  bidi_it->invalid_levels = 0;
+  bidi_it->invalid_rl_levels = -1;
+  bidi_it->next_en_pos = -1;
+  bidi_it->next_for_ws.type = UNKNOWN_BT;
+  bidi_set_sor_type (bidi_it,
+                    bidi_it->paragraph_dir == R2L ? 1 : 0,
+                    bidi_it->level_stack[0].level); /* X10 */
+
+  bidi_cache_reset ();
+}
+
+/* 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
+bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
+{
+  Lisp_Object re = paragraph_start_re;
+  EMACS_INT limit = ZV, limit_byte = ZV_BYTE;
+
+  while (pos_byte > BEGV_BYTE
+        && fast_looking_at (re, pos, pos_byte, limit, limit_byte, Qnil) < 0)
+    {
+      pos = find_next_newline_no_quit (pos - 1, -1);
+      pos_byte = CHAR_TO_BYTE (pos);
+    }
+  return pos_byte;
+}
+
+/* Determine the base direction, a.k.a. base embedding level, of the
+   paragraph we are about to iterate through.  If DIR is either L2R or
+   R2L, just use that.  Otherwise, determine the paragraph direction
+   from the first strong directional character of the paragraph.
+
+   NO_DEFAULT_P non-nil means don't default to L2R if the paragraph
+   has no strong directional characters and both DIR and
+   bidi_it->paragraph_dir are NEUTRAL_DIR.  In that case, search back
+   in the buffer until a paragraph is found with a strong character,
+   or until hitting BEGV.  In the latter case, fall back to L2R.  This
+   flag is used in current-bidi-paragraph-direction.
+
+   Note that this function gives the paragraph separator the same
+   direction as the preceding paragraph, even though Emacs generally
+   views the separartor as not belonging to any paragraph.  */
 void
-bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it)
+bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
 {
-  bidi_it->level_stack[0].level = 0;
-  if (dir == R2L)
-    bidi_it->level_stack[0].level = 1;
+  EMACS_INT bytepos = bidi_it->bytepos;
+  EMACS_INT pstartbyte;
+
+  /* Special case for an empty buffer. */
+  if (bytepos == BEGV_BYTE && bytepos == ZV_BYTE)
+    dir = L2R;
+  /* We should never be called at EOB or before BEGV.  */
+  else if (bytepos >= ZV_BYTE || bytepos < BEGV_BYTE)
+    abort ();
+
+  if (dir == L2R)
+    {
+      bidi_it->paragraph_dir = L2R;
+      bidi_it->new_paragraph = 0;
+    }
+  else if (dir == R2L)
+    {
+      bidi_it->paragraph_dir = R2L;
+      bidi_it->new_paragraph = 0;
+    }
   else if (dir == NEUTRAL_DIR) /* P2 */
     {
+      int ch, ch_len;
+      EMACS_INT pos;
       bidi_type_t type;
-      int pos = bidi_it->charpos, bytepos = bidi_it->bytepos;
-      int ch;
 
-      if (pos < 0)
-       pos = bytepos = 0;
-      else if (bidi_it->ch != BIDI_EOB)
+      if (!bidi_initialized)
+       bidi_initialize ();
+
+      /* If we are inside a paragraph separator, we are just waiting
+        for the separator to be exhausted; use the previous paragraph
+        direction.  But don't do that if we have been just reseated,
+        because we need to reinitialize below in that case.  */
+      if (!bidi_it->first_elt
+         && bidi_it->charpos < bidi_it->separator_limit)
+       return;
+
+      /* If we are on a newline, get past it to where the next
+        paragraph might start.  But don't do that at BEGV since then
+        we are potentially in a new paragraph that doesn't yet
+        exist.  */
+      pos = bidi_it->charpos;
+      if (bytepos > BEGV_BYTE && FETCH_CHAR (bytepos) == '\n')
        {
+         bytepos++;
          pos++;
-         bytepos += bidi_it->ch_len;
        }
 
-      ch = FETCH_CHAR (bytepos);
-      pos++;
-      bytepos += CHAR_BYTES (ch);
-
-      /* FIXME: should actually go to where the paragraph begins and
-        start the loop below from there, since UAX#9 says to find the
-        first strong directional character in the paragraph.  */
-
-      for (type = bidi_get_type (ch);
-          /* NOTE: UAX#9 says to search only for L, AL, or R types of
-             characters, and ignore RLE, RLO, LRE, and LRO.  However,
-             I'm not sure it makes sense to omit those 4; should try
-             with and without that to see the effect.  */
-          (bidi_get_category (type) != STRONG)
-            || (bidi_ignore_explicit_marks_for_paragraph_level
-                && (type == RLE || type == RLO
-                    || type == LRE || type == LRO));
-          type = bidi_get_type (ch))
-       {
-         if (type == NEUTRAL_B || bidi_at_paragraph_end (ch, bytepos))
-           break;
-         FETCH_CHAR_ADVANCE (ch, pos, bytepos);
-       }
-      if (type == STRONG_R || type == STRONG_AL) /* P3 */
-       bidi_it->level_stack[0].level = 1;
+      /* 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);
+      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.  */
+      do {
+       bytepos = pstartbyte;
+       ch = FETCH_CHAR (bytepos);
+       ch_len = CHAR_BYTES (ch);
+       pos = BYTE_TO_CHAR (bytepos);
+       type = bidi_get_type (ch, NEUTRAL_DIR);
+
+       for (pos++, bytepos += ch_len;
+            /* NOTE: UAX#9 says to search only for L, AL, or R types
+               of characters, and ignore RLE, RLO, LRE, and LRO.
+               However, I'm not sure it makes sense to omit those 4;
+               should try with and without that to see the effect.  */
+            (bidi_get_category (type) != STRONG)
+              || (bidi_ignore_explicit_marks_for_paragraph_level
+                  && (type == RLE || type == RLO
+                      || type == LRE || type == LRO));
+            type = bidi_get_type (ch, NEUTRAL_DIR))
+         {
+           if (type == NEUTRAL_B && bidi_at_paragraph_end (pos, bytepos) >= -1)
+             break;
+           if (bytepos >= ZV_BYTE)
+             {
+               /* Pretend there's a paragraph separator at end of
+                  buffer.  */
+               type = NEUTRAL_B;
+               break;
+             }
+           FETCH_CHAR_ADVANCE (ch, pos, bytepos);
+         }
+       if (type == STRONG_R || type == STRONG_AL) /* P3 */
+         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 this paragraph is at BEGV, default to L2R.  */
+           if (pstartbyte == BEGV_BYTE)
+             bidi_it->paragraph_dir = L2R; /* P3 and HL1 */
+           else
+             {
+               EMACS_INT prevpbyte = pstartbyte;
+               EMACS_INT p = BYTE_TO_CHAR (pstartbyte), pbyte = pstartbyte;
+
+               /* Find the beginning of the previous paragraph, if any.  */
+               while (pbyte > BEGV_BYTE && prevpbyte >= pstartbyte)
+                 {
+                   p--;
+                   pbyte = CHAR_TO_BYTE (p);
+                   prevpbyte = bidi_find_paragraph_start (p, pbyte);
+                 }
+               pstartbyte = prevpbyte;
+             }
+         }
+      } while (no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR);
     }
-  bidi_it->scan_dir = 1; /* FIXME: do we need to have control on this? */
-  bidi_it->resolved_level = bidi_it->level_stack[0].level;
-  bidi_it->level_stack[0].override = NEUTRAL_DIR; /* X1 */
-  bidi_it->invalid_levels = 0;
-  bidi_it->invalid_rl_levels = -1;
-  bidi_it->new_paragraph = 0;
-  bidi_it->next_en_pos = -1;
-  bidi_it->next_for_ws.type = UNKNOWN_BT;
-  bidi_set_sor_type (bidi_it, bidi_it->level_stack[0].level, 0); /* X10 */
+  else
+    abort ();
 
-  bidi_cache_reset ();
+  /* Contrary to UAX#9 clause P3, we only default the paragraph
+     direction to L2R if we have no previous usable paragraph
+     direction.  This is allowed by the HL1 clause.  */
+  if (bidi_it->paragraph_dir != L2R && bidi_it->paragraph_dir != R2L)
+    bidi_it->paragraph_dir = L2R; /* P3 and HL1 ``higher-level protocols'' */
+  if (bidi_it->paragraph_dir == R2L)
+    bidi_it->level_stack[0].level = 1;
+  else
+    bidi_it->level_stack[0].level = 0;
+
+  bidi_line_init (bidi_it);
 }
 
-/* Do whatever UAX#9 clause X8 says should be done at paragraph's end,
-   and set the new paragraph flag in the iterator.  */
-static inline void
+/* 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;
-  bidi_it->new_paragraph = 1;
 }
 
-/* Initialize the bidi iterator from buffer position POS for paragraph
-   direction DIR.  Return the embedding level at POS.  */
+/* Initialize the bidi iterator from buffer position CHARPOS.  */
 void
-bidi_init_it (int pos, bidi_dir_t dir, struct bidi_it *bidi_it)
+bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, struct bidi_it *bidi_it)
 {
   if (! bidi_initialized)
     bidi_initialize ();
+  bidi_it->charpos = charpos;
+  bidi_it->bytepos = bytepos;
+  bidi_it->first_elt = 1;
   bidi_set_paragraph_end (bidi_it);
-  bidi_it->charpos = pos;
-  if (pos <= 0)
-    {
-      bidi_it->bytepos = bidi_it->charpos;
-      bidi_it->ch_len = 1;     /* so that incrementing bytepos works */
-    }
-  else
-    {
-      bidi_it->bytepos = CHAR_TO_BYTE (pos);
-      bidi_it->ch_len
-       = MULTIBYTE_FORM_LENGTH (BYTE_POS_ADDR (bidi_it->bytepos),
-                                MAX_MULTIBYTE_LENGTH);
-    }
-  bidi_it->ch = '\x1d';         /* FIXME: should be U+2029 */
+  bidi_it->new_paragraph = 1;
+  bidi_it->separator_limit = -1;
   bidi_it->type = NEUTRAL_B;
-  bidi_it->orig_type = UNKNOWN_BT;
-  bidi_it->pristine_type = UNKNOWN_BT;
+  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.orig_type = UNKNOWN_BT;
-  bidi_it->last_strong.type = bidi_it->last_strong.orig_type =
-    bidi_it->last_strong.pristine_type = UNKNOWN_BT;
+  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.orig_type =
-    bidi_it->next_for_neutral.pristine_type = UNKNOWN_BT;
+    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.orig_type =
-    bidi_it->prev_for_neutral.pristine_type = UNKNOWN_BT;
+    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_paragraph_init (dir, bidi_it);
+  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
+static INLINE void
 bidi_push_embedding_level (struct bidi_it *bidi_it,
                           int level, bidi_dir_t override)
 {
@@ -906,7 +785,7 @@ bidi_push_embedding_level (struct bidi_it *bidi_it,
 
 /* Pop the embedding level and directional override status from the
    stack, and return the new level.  */
-static inline int
+static INLINE int
 bidi_pop_embedding_level (struct bidi_it *bidi_it)
 {
   /* UAX#9 says to ignore invalid PDFs.  */
@@ -916,20 +795,23 @@ bidi_pop_embedding_level (struct bidi_it *bidi_it)
 }
 
 /* Record in SAVED_INFO the information about the current character.  */
-static inline void
+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;
-  saved_info->pristine_type = bidi_it->pristine_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
+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.  */
@@ -946,7 +828,7 @@ bidi_resolve_neutral_1 (bidi_type_t prev_type, bidi_type_t next_type, int lev)
     return STRONG_R;
 }
 
-static inline int
+static INLINE int
 bidi_explicit_dir_char (int c)
 {
   /* FIXME: this should be replaced with a lookup table with suitable
@@ -968,11 +850,19 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
   int new_level;
   bidi_dir_t override;
 
-  if (bidi_it->charpos < 0)
-    bidi_it->charpos = bidi_it->bytepos = 0;
-  else
+  if (bidi_it->bytepos < BEGV_BYTE     /* after reseat to BEGV? */
+      || 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);
+    }
+  else if (bidi_it->bytepos < ZV_BYTE) /* don't move at ZV */
     {
       bidi_it->charpos++;
+      if (bidi_it->ch_len == 0)
+       abort ();
       bidi_it->bytepos += bidi_it->ch_len;
     }
 
@@ -982,23 +872,37 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
 
   /* in case it is a unibyte character (not yet implemented) */
   /* _fetch_multibyte_char_len = 1; */
-  curchar = FETCH_CHAR (bidi_it->bytepos);
+  if (bidi_it->bytepos >= ZV_BYTE)
+    {
+      curchar = BIDI_EOB;
+      bidi_it->ch_len = 1;
+    }
+  else
+    {
+      curchar = FETCH_CHAR (bidi_it->bytepos);
+      bidi_it->ch_len = CHAR_BYTES (curchar);
+    }
   bidi_it->ch = curchar;
-  bidi_it->ch_len = CHAR_BYTES (curchar);
 
-  type = bidi_get_type (curchar);
-  bidi_it->pristine_type = type;
+  /* Don't apply directional override here, as all the types we handle
+     below will not be affected by the override anyway, and we need
+     the original type unaltered.  The override will be applied in
+     bidi_resolve_weak.  */
+  type = bidi_get_type (curchar, NEUTRAL_DIR);
+  bidi_it->orig_type = type;
+  bidi_check_type (bidi_it->orig_type);
 
   if (type != PDF)
     bidi_it->prev_was_pdf = 0;
 
-  bidi_it->orig_type = UNKNOWN_BT;
+  bidi_it->type_after_w1 = UNKNOWN_BT;
 
   switch (type)
     {
       case RLE:        /* X2 */
       case RLO:        /* X4 */
-       bidi_it->orig_type = type;
+       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)
          {
@@ -1007,7 +911,7 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
                /* Compute the least odd embedding level greater than
                   the current level.  */
                new_level = ((current_level + 1) & ~1) + 1;
-               if (bidi_it->orig_type == RLE)
+               if (bidi_it->type_after_w1 == RLE)
                  override = NEUTRAL_DIR;
                else
                  override = R2L;
@@ -1024,13 +928,14 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
                bidi_it->invalid_rl_levels++;
              }
          }
-       else if (bidi_it->prev.orig_type == WEAK_EN /* W5/Retaining */
+       else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
                 || bidi_it->next_en_pos > bidi_it->charpos)
          type = WEAK_EN;
        break;
       case LRE:        /* X3 */
       case LRO:        /* X5 */
-       bidi_it->orig_type = type;
+       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)
          {
@@ -1039,7 +944,7 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
                /* Compute the least even embedding level greater than
                   the current level.  */
                new_level = ((current_level + 2) & ~1);
-               if (bidi_it->orig_type == LRE)
+               if (bidi_it->type_after_w1 == LRE)
                  override = NEUTRAL_DIR;
                else
                  override = L2R;
@@ -1059,12 +964,13 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
                  bidi_it->invalid_rl_levels++;
              }
          }
-       else if (bidi_it->prev.orig_type == WEAK_EN /* W5/Retaining */
+       else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
                 || bidi_it->next_en_pos > bidi_it->charpos)
          type = WEAK_EN;
        break;
       case PDF:        /* X7 */
-       bidi_it->orig_type = type;
+       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)
          {
@@ -1084,7 +990,7 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
                bidi_it->invalid_rl_levels--;
              }
          }
-       else if (bidi_it->prev.orig_type == WEAK_EN /* W5/Retaining */
+       else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
                 || bidi_it->next_en_pos > bidi_it->charpos)
          type = WEAK_EN;
        break;
@@ -1094,6 +1000,7 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
     }
 
   bidi_it->type = type;
+  bidi_check_type (bidi_it->type);
 
   return new_level;
 }
@@ -1112,6 +1019,7 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
   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_CHAR (bidi_it->bytepos
                                             + bidi_it->ch_len)))
     {
@@ -1157,17 +1065,12 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
        }
     }
 
-  /* For when the paragraph end is defined by anything other than a
-     special Unicode character (a.k.a. ``higher protocols'').  */
-  if (bidi_it->type != NEUTRAL_B)
-    if (bidi_at_paragraph_end (bidi_it->ch,
-                              bidi_it->bytepos + bidi_it->ch_len))
-      bidi_it->type = NEUTRAL_B;
-
   if (bidi_it->type == NEUTRAL_B)      /* X8 */
     {
       bidi_set_paragraph_end (bidi_it);
-      bidi_it->orig_type = bidi_it->type; /* needed below and in L1 */
+      /* This is needed by bidi_resolve_weak below, and in L1.  */
+      bidi_it->type_after_w1 = bidi_it->type;
+      bidi_check_type (bidi_it->type_after_w1);
     }
 
   return new_level;
@@ -1175,7 +1078,7 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
 
 /* Advance in the buffer, resolve weak types and return the type of
    the next character after weak type resolution.  */
-bidi_type_t
+static bidi_type_t
 bidi_resolve_weak (struct bidi_it *bidi_it)
 {
   bidi_type_t type;
@@ -1207,7 +1110,8 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
     }
   else if (type == NEUTRAL_S || type == NEUTRAL_WS
           || type == WEAK_BN || type == STRONG_AL)
-    bidi_it->orig_type = type; /* needed in L1 */
+    bidi_it->type_after_w1 = type;     /* needed in L1 */
+  bidi_check_type (bidi_it->type_after_w1);
 
   /* Level and directional override status are already recorded in
      bidi_it, and do not need any change; see X6.  */
@@ -1215,148 +1119,157 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
     type = STRONG_R;
   else if (override == L2R)
     type = STRONG_L;
-  else if (type == STRONG_AL)
-    type = STRONG_R;           /* W3 */
-  else if (type == WEAK_NSM)   /* W1 */
-    {
-      /* Note that we don't need to consider the case where the prev
-        character has its type overridden by an RLO or LRO: such
-        characters are outside the current level run, and thus not
-        relevant to this NSM.  Thus, NSM gets the pristine_type of
-        the previous character.  */
-      if (bidi_it->prev.type != UNKNOWN_BT)
-       type = bidi_it->prev.pristine_type;
-      else if (bidi_it->sor == R2L)
-       type = STRONG_R;
-      else if (bidi_it->sor == L2R)
-       type = STRONG_L;
-      else /* shouldn't happen! */
-       abort ();
-      if (type == WEAK_EN      /* W2 after W1 */
-         && bidi_it->last_strong.orig_type == STRONG_AL)
-       type = WEAK_AN;
-    }
-  else if (type == WEAK_EN     /* W2 */
-          && bidi_it->last_strong.orig_type == STRONG_AL)
-    type = WEAK_AN;
-  else if ((type == WEAK_ES
-           && (bidi_it->prev.orig_type == WEAK_EN              /* W4 */
-               && (bidi_it->prev.pristine_type == WEAK_EN
-                   || bidi_it->prev.pristine_type == WEAK_NSM))) /* aft W1 */
-          || (type == WEAK_CS
-              && ((bidi_it->prev.orig_type == WEAK_EN
-                   && (bidi_it->prev.pristine_type == WEAK_EN  /* W4 */
-                       || bidi_it->prev.pristine_type == WEAK_NSM)) /* a/W1 */
-                  || bidi_it->prev.orig_type == WEAK_AN)))     /* W4 */
+  else
     {
-      next_char = FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
-      type_of_next = bidi_get_type (next_char);
-
-      if (type_of_next == WEAK_BN
-         || bidi_explicit_dir_char (next_char))
+      if (type == WEAK_NSM)    /* W1 */
        {
-         bidi_copy_it (&saved_it, bidi_it);
-         while (bidi_resolve_explicit (bidi_it) == new_level
-                && bidi_it->type == WEAK_BN)
-           ;
-         type_of_next = bidi_it->type;
-         bidi_copy_it (bidi_it, &saved_it);
-       }
-
-      /* If the next character is EN, but the last strong-type
-        character is AL, that next EN will be changed to AN when we
-        process it in W2 above.  So in that case, this ES should not
-        be changed into EN.  */
-      if (type == WEAK_ES
-         && type_of_next == WEAK_EN
-         && bidi_it->last_strong.orig_type != STRONG_AL)
-       type = WEAK_EN;
-      else if (type == WEAK_CS)
-       {
-         if (bidi_it->prev.orig_type == WEAK_AN
-             && (type_of_next == WEAK_AN
-                 /* If the next character is EN, but the last
-                    strong-type character is AL, EN will be later
-                    changed to AN when we process it in W2 above.  So
-                    in that case, this ES should not be changed into
-                    EN.  */
-                 || (type_of_next == WEAK_EN
-                     && bidi_it->last_strong.orig_type == STRONG_AL)))
-           type = WEAK_AN;
-         else if (bidi_it->prev.orig_type == WEAK_EN
-                  && type_of_next == WEAK_EN
-                  && bidi_it->last_strong.orig_type != STRONG_AL)
-           type = WEAK_EN;
+         /* Note that we don't need to consider the case where the
+            prev character has its type overridden by an RLO or LRO,
+            because then either the type of this NSM would have been
+            also overridden, or the previous character is outside the
+            current level run, and thus not relevant to this NSM.
+            This is why NSM gets the type_after_w1 of the previous
+            character.  */
+         if (bidi_it->prev.type_after_w1 != UNKNOWN_BT
+             /* if type_after_w1 is NEUTRAL_B, this NSM is at sor */
+             && bidi_it->prev.type_after_w1 != NEUTRAL_B)
+           type = bidi_it->prev.type_after_w1;
+         else if (bidi_it->sor == R2L)
+           type = STRONG_R;
+         else if (bidi_it->sor == L2R)
+           type = STRONG_L;
+         else /* shouldn't happen! */
+           abort ();
        }
-    }
-  else if (type == WEAK_ET     /* W5: ET with EN before or after it */
-          || type == WEAK_BN)  /* W5/Retaining */
-    {
-      if (bidi_it->prev.orig_type == WEAK_EN /* ET/BN with EN before it */
-         || bidi_it->next_en_pos > bidi_it->charpos)
-       type = WEAK_EN;
-      /* W5: ET with EN after it.  */
-      else
+      if (type == WEAK_EN      /* W2 */
+         && bidi_it->last_strong.type_after_w1 == STRONG_AL)
+       type = WEAK_AN;
+      else if (type == STRONG_AL) /* W3 */
+       type = STRONG_R;
+      else if ((type == WEAK_ES        /* W4 */
+               && bidi_it->prev.type_after_w1 == WEAK_EN
+               && bidi_it->prev.orig_type == WEAK_EN)
+              || (type == WEAK_CS
+                  && ((bidi_it->prev.type_after_w1 == WEAK_EN
+                       && bidi_it->prev.orig_type == WEAK_EN)
+                      || bidi_it->prev.type_after_w1 == WEAK_AN)))
        {
-         int en_pos = bidi_it->charpos + 1;
-
-         next_char = FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
-         type_of_next = bidi_get_type (next_char);
+         next_char =
+           bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
+           ? BIDI_EOB : FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
+         type_of_next = bidi_get_type (next_char, override);
 
-         if (type_of_next == WEAK_ET
-             || type_of_next == WEAK_BN
+         if (type_of_next == WEAK_BN
              || bidi_explicit_dir_char (next_char))
            {
              bidi_copy_it (&saved_it, bidi_it);
              while (bidi_resolve_explicit (bidi_it) == new_level
-                    && (bidi_it->type == WEAK_BN || bidi_it->type == WEAK_ET))
+                    && bidi_it->type == WEAK_BN)
                ;
              type_of_next = bidi_it->type;
-             en_pos = bidi_it->charpos;
              bidi_copy_it (bidi_it, &saved_it);
            }
-         if (type_of_next == WEAK_EN)
+
+         /* If the next character is EN, but the last strong-type
+            character is AL, that next EN will be changed to AN when
+            we process it in W2 above.  So in that case, this ES
+            should not be changed into EN.  */
+         if (type == WEAK_ES
+             && type_of_next == WEAK_EN
+             && bidi_it->last_strong.type_after_w1 != STRONG_AL)
+           type = WEAK_EN;
+         else if (type == WEAK_CS)
+           {
+             if (bidi_it->prev.type_after_w1 == WEAK_AN
+                 && (type_of_next == WEAK_AN
+                     /* If the next character is EN, but the last
+                        strong-type character is AL, EN will be later
+                        changed to AN when we process it in W2 above.
+                        So in that case, this ES should not be
+                        changed into EN.  */
+                     || (type_of_next == WEAK_EN
+                         && bidi_it->last_strong.type_after_w1 == STRONG_AL)))
+               type = WEAK_AN;
+             else if (bidi_it->prev.type_after_w1 == WEAK_EN
+                      && type_of_next == WEAK_EN
+                      && bidi_it->last_strong.type_after_w1 != STRONG_AL)
+               type = WEAK_EN;
+           }
+       }
+      else if (type == WEAK_ET /* W5: ET with EN before or after it */
+              || type == WEAK_BN)      /* W5/Retaining */
+       {
+         if (bidi_it->prev.type_after_w1 == WEAK_EN /* ET/BN w/EN before it */
+             || bidi_it->next_en_pos > bidi_it->charpos)
+           type = WEAK_EN;
+         else                  /* W5: ET/BN with EN after it.  */
            {
-             /* If the last strong character is AL, the EN we've
-                found will become AN when we get to it (W2). */
-             if (bidi_it->last_strong.orig_type != STRONG_AL)
+             EMACS_INT en_pos = bidi_it->charpos + 1;
+
+             next_char =
+               bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
+               ? BIDI_EOB : FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
+             type_of_next = bidi_get_type (next_char, override);
+
+             if (type_of_next == WEAK_ET
+                 || type_of_next == WEAK_BN
+                 || bidi_explicit_dir_char (next_char))
                {
-                 type = WEAK_EN;
-                 /* Remember this EN position, to speed up processing
-                    of the next ETs.  */
-                 bidi_it->next_en_pos = en_pos;
+                 bidi_copy_it (&saved_it, bidi_it);
+                 while (bidi_resolve_explicit (bidi_it) == new_level
+                        && (bidi_it->type == WEAK_BN
+                            || bidi_it->type == WEAK_ET))
+                   ;
+                 type_of_next = bidi_it->type;
+                 en_pos = bidi_it->charpos;
+                 bidi_copy_it (bidi_it, &saved_it);
+               }
+             if (type_of_next == WEAK_EN)
+               {
+                 /* If the last strong character is AL, the EN we've
+                    found will become AN when we get to it (W2). */
+                 if (bidi_it->last_strong.type_after_w1 != STRONG_AL)
+                   {
+                     type = WEAK_EN;
+                     /* Remember this EN position, to speed up processing
+                        of the next ETs.  */
+                     bidi_it->next_en_pos = en_pos;
+                   }
+                 else if (type == WEAK_BN)
+                   type = NEUTRAL_ON; /* W6/Retaining */
                }
-             else if (type == WEAK_BN)
-               type = NEUTRAL_ON; /* W6/Retaining */
            }
        }
     }
 
   if (type == WEAK_ES || type == WEAK_ET || type == WEAK_CS /* W6 */
-      || (type == WEAK_BN && (bidi_it->prev.orig_type == WEAK_CS /* W6/Ret. */
-                             || bidi_it->prev.orig_type == WEAK_ES
-                             || bidi_it->prev.orig_type == WEAK_ET)))
+      || (type == WEAK_BN
+         && (bidi_it->prev.type_after_w1 == WEAK_CS        /* W6/Retaining */
+             || bidi_it->prev.type_after_w1 == WEAK_ES
+             || bidi_it->prev.type_after_w1 == WEAK_ET)))
     type = NEUTRAL_ON;
 
   /* Store the type we've got so far, before we clobber it with strong
      types in W7 and while resolving neutral types.  But leave alone
      the original types that were recorded above, because we will need
      them for the L1 clause.  */
-  if (bidi_it->orig_type == UNKNOWN_BT)
-    bidi_it->orig_type = type;
+  if (bidi_it->type_after_w1 == UNKNOWN_BT)
+    bidi_it->type_after_w1 = type;
+  bidi_check_type (bidi_it->type_after_w1);
 
   if (type == WEAK_EN) /* W7 */
     {
-      if ((bidi_it->last_strong.orig_type == STRONG_L)
+      if ((bidi_it->last_strong.type_after_w1 == STRONG_L)
          || (bidi_it->last_strong.type == UNKNOWN_BT && bidi_it->sor == L2R))
        type = STRONG_L;
     }
 
   bidi_it->type = type;
+  bidi_check_type (bidi_it->type);
   return type;
 }
 
-bidi_type_t
+static bidi_type_t
 bidi_resolve_neutral (struct bidi_it *bidi_it)
 {
   int prev_level = bidi_it->level_stack[bidi_it->stack_idx].level;
@@ -1406,7 +1319,7 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
          do {
            /* Record the info about the previous character, so that
               it will be cached below with this state.  */
-           if (bidi_it->orig_type != WEAK_BN /* W1/Retaining */
+           if (bidi_it->type_after_w1 != WEAK_BN /* W1/Retaining */
                && bidi_it->type != WEAK_BN)
              bidi_remember_char (&bidi_it->prev, bidi_it);
            type = bidi_resolve_weak (bidi_it);
@@ -1450,10 +1363,11 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
                   stored by bidi_set_sor_type in the prev_for_neutral
                   member.  */
                if (saved_it.type != WEAK_BN
-                   || bidi_get_category (bidi_it->prev.orig_type) == NEUTRAL)
+                   || bidi_get_category (bidi_it->prev.type_after_w1) == NEUTRAL)
                  {
                    next_type = bidi_it->prev_for_neutral.type;
                    saved_it.next_for_neutral.type = next_type;
+                   bidi_check_type (next_type);
                  }
                else
                  {
@@ -1469,6 +1383,7 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
          type = bidi_resolve_neutral_1 (saved_it.prev_for_neutral.type,
                                         next_type, current_level);
          saved_it.type = type;
+         bidi_check_type (type);
          bidi_copy_it (bidi_it, &saved_it);
        }
     }
@@ -1478,7 +1393,7 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
 /* Given an iterator state in BIDI_IT, advance one character position
    in the buffer to the next character (in the logical order), resolve
    the bidi type of that next character, and return that type.  */
-bidi_type_t
+static bidi_type_t
 bidi_type_of_next_char (struct bidi_it *bidi_it)
 {
   bidi_type_t type;
@@ -1504,7 +1419,7 @@ bidi_type_of_next_char (struct bidi_it *bidi_it)
    the buffer to the next character (in the logical order), resolve
    the embedding and implicit levels of that next character, and
    return the resulting level.  */
-int
+static int
 bidi_level_of_next_char (struct bidi_it *bidi_it)
 {
   bidi_type_t type;
@@ -1514,15 +1429,16 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
   if (bidi_it->scan_dir == 1)
     {
       /* There's no sense in trying to advance if we hit end of text.  */
-      if (bidi_it->ch == BIDI_EOB)
+      if (bidi_it->bytepos >= ZV_BYTE)
        return bidi_it->resolved_level;
 
       /* Record the info about the previous character.  */
-      if (bidi_it->orig_type != WEAK_BN /* W1/Retaining */
+      if (bidi_it->type_after_w1 != WEAK_BN /* W1/Retaining */
          && bidi_it->type != WEAK_BN)
        bidi_remember_char (&bidi_it->prev, bidi_it);
-      if (bidi_it->orig_type == STRONG_R || bidi_it->orig_type == STRONG_L
-         || bidi_it->orig_type == STRONG_AL)
+      if (bidi_it->type_after_w1 == STRONG_R
+         || bidi_it->type_after_w1 == STRONG_L
+         || bidi_it->type_after_w1 == STRONG_AL)
        bidi_remember_char (&bidi_it->last_strong, bidi_it);
       /* FIXME: it sounds like we don't need both prev and
         prev_for_neutral members, but I'm leaving them both for now.  */
@@ -1601,32 +1517,34 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
        || type == WEAK_AN))
     abort ();
   bidi_it->type = type;
+  bidi_check_type (bidi_it->type);
 
   /* For L1 below, we need to know, for each WS character, whether
-     it belongs to a sequence of WS characters preceeding a newline
+     it belongs to a sequence of WS characters preceding a newline
      or a TAB or a paragraph separator.  */
-  if (bidi_it->pristine_type == NEUTRAL_WS
+  if (bidi_it->orig_type == NEUTRAL_WS
       && bidi_it->next_for_ws.type == UNKNOWN_BT)
     {
       int ch;
       int clen = bidi_it->ch_len;
-      int bpos = bidi_it->bytepos;
-      int cpos = bidi_it->charpos;
+      EMACS_INT bpos = bidi_it->bytepos;
+      EMACS_INT cpos = bidi_it->charpos;
       bidi_type_t chtype;
 
       do {
        /*_fetch_multibyte_char_len = 1;*/
-       ch = FETCH_CHAR (bpos + clen);
+       ch = bpos + clen >= ZV_BYTE ? BIDI_EOB : FETCH_CHAR (bpos + clen);
        bpos += clen;
        cpos++;
-       clen = CHAR_BYTES (ch);
-       if (ch == '\n' /* || ch == LINESEP_CHAR */)
+       clen = (ch == BIDI_EOB ? 1 : CHAR_BYTES (ch));
+       if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */)
          chtype = NEUTRAL_B;
        else
-         chtype = bidi_get_type (ch);
+         chtype = bidi_get_type (ch, NEUTRAL_DIR);
       } while (chtype == NEUTRAL_WS || chtype == WEAK_BN
               || bidi_explicit_dir_char (ch)); /* L1/Retaining */
       bidi_it->next_for_ws.type = chtype;
+      bidi_check_type (bidi_it->next_for_ws.type);
       bidi_it->next_for_ws.charpos = cpos;
       bidi_it->next_for_ws.bytepos = bpos;
     }
@@ -1634,7 +1552,7 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
   /* Resolve implicit levels, with a twist: PDFs get the embedding
      level of the enbedding they terminate.  See below for the
      reason.  */
-  if (bidi_it->pristine_type == PDF
+  if (bidi_it->orig_type == PDF
       /* Don't do this if this formatting code didn't change the
         embedding level due to invalid or empty embeddings.  */
       && prev_level != level)
@@ -1648,7 +1566,7 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
 
         we want it to be displayed as
 
-            {RLO}STet{PDF}
+            {PDF}STet{RLO}
 
         not as
 
@@ -1662,14 +1580,15 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
         ugly side effect does not happen.
 
         (This is, of course, only important if the formatting codes
-        are actually displayed, but Emacs does display them if the
-        user wants to.)     */
+        are actually displayed, but Emacs does need to display them
+        if the user wants to.)  */
       level = prev_level;
     }
-  else if (bidi_it->pristine_type == NEUTRAL_B /* L1 */
-          || bidi_it->pristine_type == NEUTRAL_S
-          || bidi_it->ch == '\n' /* || bidi_it->ch == LINESEP_CHAR */
-          || (bidi_it->pristine_type == NEUTRAL_WS
+  else if (bidi_it->orig_type == NEUTRAL_B /* L1 */
+          || bidi_it->orig_type == NEUTRAL_S
+          || bidi_it->ch == '\n' || bidi_it->ch == BIDI_EOB
+          /* || bidi_it->ch == LINESEP_CHAR */
+          || (bidi_it->orig_type == NEUTRAL_WS
               && (bidi_it->next_for_ws.type == NEUTRAL_B
                   || bidi_it->next_for_ws.type == NEUTRAL_S)))
     level = bidi_it->level_stack[0].level;
@@ -1735,29 +1654,42 @@ bidi_find_other_level_edge (struct bidi_it *bidi_it, int level, int end_flag)
 }
 
 void
-bidi_get_next_char_visually (struct bidi_it *bidi_it)
+bidi_move_to_visually_next (struct bidi_it *bidi_it)
 {
   int old_level, new_level, next_level;
-  struct bidi_it prev_bidi_it;
+  struct bidi_it sentinel;
 
   if (bidi_it->scan_dir == 0)
     {
       bidi_it->scan_dir = 1;   /* default to logical order */
     }
 
-  if (bidi_it->new_paragraph)
-    bidi_paragraph_init (bidi_overriding_paragraph_direction, bidi_it);
+  /* 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);
+
+  /* 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)
-    bidi_copy_it (&prev_bidi_it, bidi_it);
+    {
+      bidi_copy_it (&sentinel, bidi_it);
+      if (bidi_it->first_elt)
+       {
+         sentinel.charpos--;   /* cached charpos needs to be monotonic */
+         sentinel.bytepos--;
+         sentinel.ch = '\n';   /* doesn't matter, but why not? */
+         sentinel.ch_len = 1;
+       }
+      bidi_cache_iterator_state (&sentinel, 1);
+    }
 
   old_level = bidi_it->resolved_level;
   new_level = bidi_level_of_next_char (bidi_it);
-  if (bidi_it->ch == BIDI_EOB)
-    return;
 
   /* Reordering of resolved levels (clause L2) is implemented by
      jumping to the other edge of the level and flipping direction of
-     scanning the buffer whenever we find a level change.  */
+     scanning the text whenever we find a level change.  */
   if (new_level != old_level)
     {
       int ascending = new_level > old_level;
@@ -1765,11 +1697,6 @@ bidi_get_next_char_visually (struct bidi_it *bidi_it)
       int incr = ascending ? 1 : -1;
       int expected_next_level = old_level + incr;
 
-      /* If we don't have anything cached yet, we need to cache the
-        previous character we've seen, since we'll need it to record
-        where to jump when the last non-base level is exhausted.  */
-      if (bidi_cache_idx == 0)
-       bidi_cache_iterator_state (&prev_bidi_it, 1);
       /* Jump (or walk) to the other edge of this level.  */
       bidi_find_other_level_edge (bidi_it, level_to_search, !ascending);
       /* Switch scan direction and peek at the next character in the
@@ -1809,6 +1736,31 @@ bidi_get_next_char_visually (struct bidi_it *bidi_it)
       next_level = bidi_level_of_next_char (bidi_it);
     }
 
+  /* Take note when we have just processed the newline that precedes
+     the end of the paragraph.  The next time we are about to be
+     called, set_iterator_to_next will automatically reinit the
+     paragraph direction, if needed.  We do this at the newline before
+     the paragraph separator, because the next character might not be
+     the first character of the next paragraph, due to the bidi
+     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)
+    {
+      EMACS_INT sep_len =
+       bidi_at_paragraph_end (bidi_it->charpos + 1,
+                              bidi_it->bytepos + bidi_it->ch_len);
+      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 + 1 + sep_len;
+       }
+    }
+
   if (bidi_it->scan_dir == 1 && bidi_cache_idx)
     {
       /* If we are at paragraph's base embedding level and beyond the
@@ -1828,6 +1780,7 @@ bidi_get_next_char_visually (struct bidi_it *bidi_it)
 
 /* This is meant to be called from within the debugger, whenever you
    wish to examine the cache contents.  */
+void bidi_dump_cached_states (void) EXTERNALLY_VISIBLE;
 void
 bidi_dump_cached_states (void)
 {
@@ -1854,283 +1807,6 @@ bidi_dump_cached_states (void)
   fputs ("\n", stderr);
   fputs ("pos ", stderr);
   for (i = 0; i < bidi_cache_idx; i++)
-    fprintf (stderr, "%*d", ndigits, bidi_cache[i].charpos);
+    fprintf (stderr, "%*"pI"d", ndigits, bidi_cache[i].charpos);
   fputs ("\n", stderr);
 }
-
-#ifdef TEST_STANDALONE
-
-#include <sys/stat.h>
-#include <signal.h>
-
-static char display_line[80];
-static int simulate_display;
-static int incr = 1;
-
-void
-signal_catcher (int sig)
-{
-  if (simulate_display)
-    puts (display_line);
-  else
-    {
-      puts ("<");
-      fflush (stdout);
-    }
-  signal (sig, SIG_DFL);
-  raise (sig);
-}
-
-void
-put_line (char *p)
-{
-  if (simulate_display)
-    {
-      if (incr == -1)
-       {
-         if (p >= display_line)
-           memset (display_line, ' ', p - display_line + 1);
-       }
-      else
-       *p = '\0';
-      fputs (display_line, stdout);
-    }
-  fflush (stdout);
-}
-
-char *
-init_display_direction (bidi_dir_t default_dir, int base_level)
-{
-  char *p;
-
-  /* To which display margin should we flush the lines?  */
-  switch (default_dir)
-    {
-      case NEUTRAL_DIR:
-       if ((base_level & 1) == 0)
-         {
-           p = display_line;
-           incr = 1;
-         }
-       else
-         {
-           p = display_line + sizeof (display_line) - 1;
-           *p-- = '\0';
-           incr = -1;
-         }
-       break;
-      case L2R:
-       p = display_line;
-       incr = 1;
-       break;
-      case R2L:
-       p = display_line + sizeof (display_line) - 1;
-       *p-- = '\0';
-       incr = -1;
-       break;
-      default:
-       abort ();
-    }
-
-  return p;
-}
-
-static char *
-continuation_line (char *p, int need)
-{
-  if (incr == -1)
-    {
-      if (p < display_line + need)
-       {
-         *p-- = '/';
-         put_line (p);
-         putc ('\n', stdout);
-         memset (display_line, '>', sizeof(display_line) - 1);
-         p = display_line + sizeof (display_line) - 1;
-         *p-- = '\0';
-       }
-    }
-  else
-    {
-      if (p > display_line + sizeof(display_line) - need - 2)
-       {
-         *p++ = '\\';
-         put_line (p);
-         putc ('\n', stdout);
-         memset (display_line, '<', sizeof(display_line) - 1);
-         p = display_line;
-       }
-    }
-
-  return p;
-}
-
-int main (int argc, char *argv[])
-{
-  bidi_dir_t default_dir = NEUTRAL_DIR;
-  char lots_of_equals[] = "\n===============================================================================\n";
-
-
-  if (argc > 1 && argv[1][0] == '-')
-    {
-      switch (argv[1][1])
-       {
-         case 'R':
-           default_dir = R2L;
-           simulate_display = 1;
-           ++argv;
-           break;
-         case 'L':
-           default_dir = L2R;
-           simulate_display = 1;
-           ++argv;
-           break;
-         case 'N':
-           simulate_display = 1;
-           ++argv;
-           break;
-         default:
-           break;
-       }
-      bidi_overriding_paragraph_direction = default_dir;
-    }
-
-  for (argv++; *argv; argv++)
-    {
-      FILE *in = fopen (*argv, "rb");
-      struct stat stat_buf;
-      struct bidi_it iterator;
-      size_t i;
-      char *p = display_line;
-      int base_level;
-      unsigned char *s, *d, *s_end;
-
-      if (!in || stat (*argv, &stat_buf))
-       {
-         perror (*argv);
-         continue;
-       }
-
-      if (stat_buf.st_size > input_buf_size)
-       {
-         input_buf = realloc (input_buf, stat_buf.st_size + 1);
-         if (!input_buf)
-           {
-             perror ("realloc input buffer");
-             continue;
-           }
-         input_buf_size = stat_buf.st_size;
-       }
-      if (fread (input_buf, 1, stat_buf.st_size, in) != stat_buf.st_size)
-       {
-         perror ("reading input");
-         continue;
-       }
-      input_buf[stat_buf.st_size] = '\0';
-      for (d = s = input_buf, s_end = s + stat_buf.st_size - 1; *s; s++)
-       {
-         if (*s != '\r' || s >= s_end || s[1] != '\n')
-           *d++ = *s;
-       }
-      stat_buf.st_size = d - input_buf;
-      input_buf[stat_buf.st_size] = '\0';
-
-      /* Done with administrivia, now for some real work...  */
-      signal (SIGABRT, signal_catcher);
-      signal (SIGINT, signal_catcher);
-      bidi_init_it (-1, default_dir, &iterator);
-      if (simulate_display)
-       {
-         p = init_display_direction (default_dir,
-                                     iterator.level_stack[0].level);
-       }
-
-      memset (display_line, incr == -1 ? '>' : '<', sizeof (display_line) - 1);
-      display_line[sizeof (display_line) - 1] = '\0';
-      base_level = iterator.level_stack[0].level;
-
-      for (i = 0; i <= stat_buf.st_size; i++)
-       {
-         int c;
-
-         bidi_get_next_char_visually (&iterator);
-         c = iterator.ch;
-
-         if (c == '\n' || c == BIDI_EOB)
-           {
-             if (simulate_display)
-               {
-                 put_line (p);
-                 /* FIXME: if -R or -L, need to init paragraph here.  */
-               }
-             if (c == BIDI_EOB)
-               break;
-             putc (c, stdout);
-           }
-         else if (c >= LRE_CHAR && c <= LRM_CHAR)
-           {
-             if (simulate_display)
-               {
-                 p = continuation_line (p, 5);
-                 if (incr == -1)
-                   {
-                     memcpy (p - 4, bidi_name[c], 5);
-                     p -= 5;
-                   }
-                 else
-                   {
-                     memcpy (p, bidi_name[c], 5);
-                     p += 5;
-                   }
-               }
-             else
-               fputs (bidi_name[c], stdout);
-           }
-         else if (c < ' ')
-           {
-             if (simulate_display)
-               {
-                 p = continuation_line (p, 2);
-                 if (incr == -1)
-                   {
-                     *p-- = '@' + c;
-                     *p-- = '^';
-                   }
-                 else
-                   {
-                     *p++ = '^';
-                     *p++ = '@' + c;
-                   }
-               }
-             else
-               printf ("^%c", (c | 0x40));
-           }
-         else
-           {
-             int c1 = (iterator.type == STRONG_R) ? bidi_mirror_char (c) : c;
-
-             if (simulate_display)
-               {
-                 p = continuation_line (p, 1);
-                 *p = c1;
-                 p += incr;
-               }
-             else
-               putc (c1, stdout);
-           }
-
-         if (iterator.ch == '\n')
-           {
-             if (base_level != iterator.level_stack[0].level)
-               base_level = iterator.level_stack[0].level;
-             p = init_display_direction (default_dir, base_level);
-             memset (display_line, incr == -1 ? '>' : '<',
-                     sizeof (display_line) - 1);
-           }
-       }
-      fputs (lots_of_equals, stdout);
-      fclose (in);
-    }
-  return 0;
-}
-#endif