aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2023-02-16 23:57:01 +0800
committerPo Lu2023-02-16 23:57:01 +0800
commit2dcce30290dc7782e9de3b4adf59f38b42408c98 (patch)
tree67b79ee1049c8fa31c8fdc2c9c67df77ac4e86ee /src
parentcf24b61985c26cbf2e5a24cb0b64a8528aa3a9cc (diff)
downloademacs-2dcce30290dc7782e9de3b4adf59f38b42408c98.tar.gz
emacs-2dcce30290dc7782e9de3b4adf59f38b42408c98.zip
Update Android port
* doc/emacs/android.texi (Android Fonts): * doc/emacs/input.texi (On-Screen Keyboards): * doc/lispref/commands.texi (Misc Events): Update documentation. * java/org/gnu/emacs/EmacsInputConnection.java (setSelection): New function. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) (reconfigureFrontBuffer): Make bitmap references weak references. * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Don't clear surfaceView bitmap. * lisp/comint.el (comint-mode): * lisp/international/fontset.el (script-representative-chars) (setup-default-fontset): Improve detection of CJK fonts. * lisp/isearch.el (set-text-conversion-style): New variable. (isearch-mode, isearch-done): Save and restore the text conversion style. * lisp/minibuffer.el (minibuffer-mode): Set an appropriate text conversion style. * lisp/simple.el (analyze-text-conversion): Run post-self-insert-hook properly. * lisp/subr.el (read-char-from-minibuffer): Disable text conversion when reading character. * src/androidterm.c (show_back_buffer): Don't check that F is not garbaged. (android_update_selection, android_reset_conversion): Use the ephemeral last point and handle text conversion being disabled. * src/buffer.c (syms_of_buffer): Convert old style DEFVAR. * src/keyboard.c (kbd_buffer_get_event): Handle text conversion first. * src/lisp.h: Update prototypes. * src/lread.c (read_filtered_event): Temporarily disable text conversion. * src/sfnt.c (sfnt_decompose_glyph_1, sfnt_decompose_glyph_2): New functions. (sfnt_decompose_glyph, sfnt_decompose_instructed_outline): Refactor contour decomposition to those two functions. (main): Update tests. * src/sfntfont-android.c (system_font_directories): Add empty field. (Fandroid_enumerate_fonts, init_sfntfont_android): Enumerate fonts in a user fonts directory. * src/sfntfont.c (struct sfnt_font_desc): New field `num_glyphs'. (sfnt_enum_font_1): Set num_glyphs and avoid duplicate fonts. (sfntfont_glyph_valid): New function. (sfntfont_lookup_char, sfntfont_list_1): Make sure glyphs found are valid. * src/textconv.c (sync_overlay, really_commit_text) (really_set_composing_text, really_set_composing_region) (really_delete_surrounding_text, really_set_point_and_mark) (handle_pending_conversion_events_1) (handle_pending_conversion_events, conversion_disabled_p) (disable_text_conversion, resume_text_conversion) (Fset_text_conversion_style, syms_of_textconv): Update to respect new options. * src/textconv.h: * src/window.h (GCALIGNED_STRUCT): New field `ephemeral_last_point'. * src/xdisp.c (mark_window_display_accurate_1): Set it.
Diffstat (limited to 'src')
-rw-r--r--src/androidterm.c25
-rw-r--r--src/buffer.c22
-rw-r--r--src/keyboard.c39
-rw-r--r--src/lisp.h2
-rw-r--r--src/lread.c26
-rw-r--r--src/sfnt.c625
-rw-r--r--src/sfntfont-android.c13
-rw-r--r--src/sfntfont.c87
-rw-r--r--src/textconv.c234
-rw-r--r--src/textconv.h1
-rw-r--r--src/window.h15
-rw-r--r--src/xdisp.c3
12 files changed, 702 insertions, 390 deletions
diff --git a/src/androidterm.c b/src/androidterm.c
index 0c990d3d2d2..c6f75ec9219 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -258,10 +258,6 @@ show_back_buffer (struct frame *f)
258{ 258{
259 struct android_swap_info swap_info; 259 struct android_swap_info swap_info;
260 260
261 /* Somehow Android frames can be swapped while garbaged. */
262 if (FRAME_GARBAGED_P (f))
263 return;
264
265 memset (&swap_info, 0, sizeof (swap_info)); 261 memset (&swap_info, 0, sizeof (swap_info));
266 swap_info.swap_window = FRAME_ANDROID_WINDOW (f); 262 swap_info.swap_window = FRAME_ANDROID_WINDOW (f);
267 swap_info.swap_action = ANDROID_COPIED; 263 swap_info.swap_action = ANDROID_COPIED;
@@ -5128,7 +5124,8 @@ android_update_selection (struct frame *f, struct window *w)
5128 /* Figure out where the point and mark are. If the mark is not 5124 /* Figure out where the point and mark are. If the mark is not
5129 active, then point is set to equal mark. */ 5125 active, then point is set to equal mark. */
5130 b = XBUFFER (w->contents); 5126 b = XBUFFER (w->contents);
5131 point = min (w->last_point, TYPE_MAXIMUM (jint)); 5127 point = min (w->ephemeral_last_point,
5128 TYPE_MAXIMUM (jint));
5132 mark = ((!NILP (BVAR (b, mark_active)) 5129 mark = ((!NILP (BVAR (b, mark_active))
5133 && w->last_mark != -1) 5130 && w->last_mark != -1)
5134 ? min (w->last_mark, TYPE_MAXIMUM (jint)) 5131 ? min (w->last_mark, TYPE_MAXIMUM (jint))
@@ -5150,6 +5147,7 @@ android_reset_conversion (struct frame *f)
5150 enum android_ic_mode mode; 5147 enum android_ic_mode mode;
5151 struct window *w; 5148 struct window *w;
5152 struct buffer *buffer; 5149 struct buffer *buffer;
5150 Lisp_Object style;
5153 5151
5154 /* Reset the input method. 5152 /* Reset the input method.
5155 5153
@@ -5160,19 +5158,20 @@ android_reset_conversion (struct frame *f)
5160 w = XWINDOW (f->selected_window); 5158 w = XWINDOW (f->selected_window);
5161 buffer = XBUFFER (WINDOW_BUFFER (w)); 5159 buffer = XBUFFER (WINDOW_BUFFER (w));
5162 5160
5163 if (NILP (BVAR (buffer, text_conversion_style))) 5161 style = (EQ (find_symbol_value (Qoverriding_text_conversion_style),
5162 Qlambda)
5163 ? BVAR (buffer, text_conversion_style)
5164 : find_symbol_value (Qoverriding_text_conversion_style));
5165
5166 if (NILP (style) || conversion_disabled_p ())
5164 mode = ANDROID_IC_MODE_NULL; 5167 mode = ANDROID_IC_MODE_NULL;
5165 else if (EQ (BVAR (buffer, text_conversion_style), 5168 else if (EQ (style, Qaction) || EQ (f->selected_window,
5166 Qaction)) 5169 f->minibuffer_window))
5167 mode = ANDROID_IC_MODE_ACTION; 5170 mode = ANDROID_IC_MODE_ACTION;
5168 else 5171 else
5169 mode = ANDROID_IC_MODE_TEXT; 5172 mode = ANDROID_IC_MODE_TEXT;
5170 5173
5171 android_reset_ic (FRAME_ANDROID_WINDOW (f), 5174 android_reset_ic (FRAME_ANDROID_WINDOW (f), mode);
5172 (EQ (f->selected_window,
5173 f->minibuffer_window)
5174 ? ANDROID_IC_MODE_ACTION
5175 : mode));
5176 5175
5177 /* Move its selection to the specified position. */ 5176 /* Move its selection to the specified position. */
5178 android_update_selection (f, NULL); 5177 android_update_selection (f, NULL);
diff --git a/src/buffer.c b/src/buffer.c
index af4aa583c96..393b8c5340a 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5862,15 +5862,19 @@ Use Custom to set this variable and update the display. */);
5862 DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer, 5862 DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer,
5863 text_conversion_style), 5863 text_conversion_style),
5864 Qnil, 5864 Qnil,
5865 "How the on screen keyboard's input method should insert in this buffer.\n\ 5865 doc: /* How the on screen keyboard's input method should insert in this buffer.
5866When nil, the input method will be disabled and an ordinary keyboard\n\ 5866When nil, the input method will be disabled and an ordinary keyboard
5867will be displayed in its place.\n\ 5867will be displayed in its place.
5868When the symbol `action', the input method will insert text directly, but\n\ 5868When the symbol `action', the input method will insert text directly, but
5869will send `return' key events instead of inserting new line characters.\n\ 5869will send `return' key events instead of inserting new line characters.
5870Any other value means that the input method will insert text directly.\n\ 5870Any other value means that the input method will insert text directly.
5871\n\ 5871
5872This variable does not take immediate effect when set; rather, it takes\n\ 5872If you need to make non-buffer local changes to this variable, use
5873effect upon the next redisplay after the selected window or buffer changes."); 5873`overriding-text-conversion-style', which see.
5874
5875This variable does not take immediate effect when set; rather, it
5876takes effect upon the next redisplay after the selected window or
5877buffer changes. */);
5874 5878
5875 DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions, 5879 DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
5876 doc: /* List of functions called with no args to query before killing a buffer. 5880 doc: /* List of functions called with no args to query before killing a buffer.
diff --git a/src/keyboard.c b/src/keyboard.c
index 538fdffc199..a8062adc468 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4050,14 +4050,6 @@ kbd_buffer_get_event (KBOARD **kbp,
4050 x_handle_pending_selection_requests (); 4050 x_handle_pending_selection_requests ();
4051#endif 4051#endif
4052 4052
4053#ifdef HAVE_TEXT_CONVERSION
4054 /* Handle pending ``text conversion'' requests from an input
4055 method. */
4056
4057 if (had_pending_conversion_events)
4058 handle_pending_conversion_events ();
4059#endif
4060
4061 if (CONSP (Vunread_command_events)) 4053 if (CONSP (Vunread_command_events))
4062 { 4054 {
4063 Lisp_Object first; 4055 Lisp_Object first;
@@ -4067,6 +4059,24 @@ kbd_buffer_get_event (KBOARD **kbp,
4067 return first; 4059 return first;
4068 } 4060 }
4069 4061
4062#ifdef HAVE_TEXT_CONVERSION
4063 /* There are pending text conversion operations. Text conversion
4064 events should be generated before processing any other keyboard
4065 input. */
4066 if (had_pending_conversion_events)
4067 {
4068 handle_pending_conversion_events ();
4069 obj = Qtext_conversion;
4070
4071 /* See the comment in handle_pending_conversion_events_1.
4072 Note that in addition, text conversion events are not
4073 generated if no edits were actually made. */
4074 if (conversion_disabled_p ()
4075 || NILP (Vtext_conversion_edits))
4076 obj = Qnil;
4077 }
4078 else
4079#endif
4070 /* At this point, we know that there is a readable event available 4080 /* At this point, we know that there is a readable event available
4071 somewhere. If the event queue is empty, then there must be a 4081 somewhere. If the event queue is empty, then there must be a
4072 mouse movement enabled and available. */ 4082 mouse movement enabled and available. */
@@ -4415,24 +4425,11 @@ kbd_buffer_get_event (KBOARD **kbp,
4415 else if (had_pending_selection_requests) 4425 else if (had_pending_selection_requests)
4416 obj = Qnil; 4426 obj = Qnil;
4417#endif 4427#endif
4418#ifdef HAVE_TEXT_CONVERSION
4419 /* This is an internal event used to prevent Emacs from becoming
4420 idle immediately after a text conversion operation. */
4421 else if (had_pending_conversion_events)
4422 obj = Qtext_conversion;
4423#endif
4424 else 4428 else
4425 /* We were promised by the above while loop that there was 4429 /* We were promised by the above while loop that there was
4426 something for us to read! */ 4430 something for us to read! */
4427 emacs_abort (); 4431 emacs_abort ();
4428 4432
4429#ifdef HAVE_TEXT_CONVERSION
4430 /* While not implemented as keyboard commands, changes made by the
4431 input method still mean that Emacs is no longer idle. */
4432 if (had_pending_conversion_events)
4433 timer_stop_idle ();
4434#endif
4435
4436 input_pending = readable_events (0); 4433 input_pending = readable_events (0);
4437 4434
4438 Vlast_event_frame = internal_last_event_frame; 4435 Vlast_event_frame = internal_last_event_frame;
diff --git a/src/lisp.h b/src/lisp.h
index a39ca8cc541..56ef338a5b1 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -5234,6 +5234,8 @@ extern void reset_frame_state (struct frame *);
5234extern void report_selected_window_change (struct frame *); 5234extern void report_selected_window_change (struct frame *);
5235extern void report_point_change (struct frame *, struct window *, 5235extern void report_point_change (struct frame *, struct window *,
5236 struct buffer *); 5236 struct buffer *);
5237extern void disable_text_conversion (void);
5238extern void resume_text_conversion (void);
5237extern void syms_of_textconv (void); 5239extern void syms_of_textconv (void);
5238#endif 5240#endif
5239 5241
diff --git a/src/lread.c b/src/lread.c
index 6abcea556bf..150d8a01e10 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -672,7 +672,11 @@ static void substitute_in_interval (INTERVAL, void *);
672 if the character warrants that. 672 if the character warrants that.
673 673
674 If SECONDS is a number, wait that many seconds for input, and 674 If SECONDS is a number, wait that many seconds for input, and
675 return Qnil if no input arrives within that time. */ 675 return Qnil if no input arrives within that time.
676
677 If text conversion is enabled and ASCII_REQUIRED && ERROR_NONASCII,
678 temporarily disable any input method which wants to perform
679 edits. */
676 680
677static Lisp_Object 681static Lisp_Object
678read_filtered_event (bool no_switch_frame, bool ascii_required, 682read_filtered_event (bool no_switch_frame, bool ascii_required,
@@ -680,12 +684,28 @@ read_filtered_event (bool no_switch_frame, bool ascii_required,
680{ 684{
681 Lisp_Object val, delayed_switch_frame; 685 Lisp_Object val, delayed_switch_frame;
682 struct timespec end_time; 686 struct timespec end_time;
687#ifdef HAVE_TEXT_CONVERSION
688 specpdl_ref count;
689#endif
683 690
684#ifdef HAVE_WINDOW_SYSTEM 691#ifdef HAVE_WINDOW_SYSTEM
685 if (display_hourglass_p) 692 if (display_hourglass_p)
686 cancel_hourglass (); 693 cancel_hourglass ();
687#endif 694#endif
688 695
696#ifdef HAVE_TEXT_CONVERSION
697 count = SPECPDL_INDEX ();
698
699 /* Don't use text conversion when trying to just read a
700 character. */
701
702 if (ascii_required && error_nonascii)
703 {
704 disable_text_conversion ();
705 record_unwind_protect_void (resume_text_conversion);
706 }
707#endif
708
689 delayed_switch_frame = Qnil; 709 delayed_switch_frame = Qnil;
690 710
691 /* Compute timeout. */ 711 /* Compute timeout. */
@@ -761,7 +781,11 @@ read_filtered_event (bool no_switch_frame, bool ascii_required,
761 781
762#endif 782#endif
763 783
784#ifdef HAVE_TEXT_CONVERSION
785 return unbind_to (count, val);
786#else
764 return val; 787 return val;
788#endif
765} 789}
766 790
767DEFUN ("read-char", Fread_char, Sread_char, 0, 3, 0, 791DEFUN ("read-char", Fread_char, Sread_char, 0, 3, 0,
diff --git a/src/sfnt.c b/src/sfnt.c
index 38f6984b93c..b6f4a48ea8b 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -2708,6 +2708,281 @@ sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2,
2708 result->y = control1->y + ((control2->y - control1->y) >> 1); 2708 result->y = control1->y + ((control2->y - control1->y) >> 1);
2709} 2709}
2710 2710
2711/* Decompose contour data inside X, Y and FLAGS, between the indices
2712 HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
2713 with DCONTEXT as an argument. Apply SCALE to each point; SCALE
2714 should be the factor necessary to turn points into 16.16 fixed
2715 point.
2716
2717 Value is 1 upon failure, else 0. */
2718
2719static int
2720sfnt_decompose_glyph_1 (size_t here, size_t last,
2721 sfnt_move_to_proc move_to,
2722 sfnt_line_to_proc line_to,
2723 sfnt_curve_to_proc curve_to,
2724 void *dcontext,
2725 sfnt_fword *x,
2726 sfnt_fword *y, unsigned char *flags,
2727 int scale)
2728{
2729 struct sfnt_point control1, control2, start, mid;
2730 size_t i;
2731
2732 /* The contour is empty. */
2733
2734 if (here == last)
2735 return 1;
2736
2737 /* Move the pen to the start of the contour. Apparently some fonts
2738 have off the curve points as the start of a contour, so when that
2739 happens lerp between the first and last points. */
2740
2741 if (flags[here] & 01) /* On Curve */
2742 {
2743 control1.x = x[here] * scale;
2744 control1.y = y[here] * scale;
2745 start = control1;
2746 }
2747 else if (flags[last] & 01)
2748 {
2749 /* Start at the last point if it is on the curve. Here, the
2750 start really becomes the middle of a spline. */
2751 control1.x = x[last] * scale;
2752 control1.y = y[last] * scale;
2753 start = control1;
2754
2755 /* Curve back one point early. */
2756 last -= 1;
2757 here -= 1;
2758 }
2759 else
2760 {
2761 /* Lerp between the start and the end. */
2762 control1.x = x[here] * scale;
2763 control1.y = y[here] * scale;
2764 control2.x = x[last] * scale;
2765 control2.y = y[last] * scale;
2766 sfnt_lerp_half (&control1, &control2, &start);
2767
2768 /* In either of these cases, start iterating from just here as
2769 opposed to here + 1, since logically the contour now starts
2770 from the last curve. */
2771 here -= 1;
2772 }
2773
2774 /* Move to the start. */
2775 move_to (start, dcontext);
2776
2777 /* Now handle each point between here + 1 and last. */
2778
2779 i = here;
2780 while (++i <= last)
2781 {
2782 /* If the point is on the curve, then draw a line here from the
2783 last control point. */
2784
2785 if (flags[i] & 01)
2786 {
2787 control1.x = x[i] * scale;
2788 control1.y = y[i] * scale;
2789
2790 line_to (control1, dcontext);
2791
2792 /* Move to the next point. */
2793 continue;
2794 }
2795
2796 /* Off the curve points are more interesting. They are handled
2797 one by one, with points in between being interpolated, until
2798 either the last point is reached or an on-curve point is
2799 processed. First, load the initial control points. */
2800
2801 control1.x = x[i] * scale;
2802 control1.y = y[i] * scale;
2803
2804 while (++i <= last)
2805 {
2806 /* Load this point. */
2807 control2.x = x[i] * scale;
2808 control2.y = y[i] * scale;
2809
2810 /* If this point is on the curve, curve directly to this
2811 point. */
2812
2813 if (flags[i] & 01)
2814 {
2815 curve_to (control1, control2, dcontext);
2816 goto continue_loop;
2817 }
2818
2819 /* Calculate the point between here and the previous
2820 point. */
2821 sfnt_lerp_half (&control1, &control2, &mid);
2822
2823 /* Curve over there. */
2824 curve_to (control1, mid, dcontext);
2825
2826 /* Reload the control point. */
2827 control1 = control2;
2828 }
2829
2830 /* Close the contour by curving back to start. */
2831 curve_to (control1, start, dcontext);
2832
2833 /* Don't close the contour twice. */
2834 goto exit;
2835
2836 continue_loop:
2837 continue;
2838 }
2839
2840 /* Close the contour with a line back to start. */
2841 line_to (start, dcontext);
2842
2843 exit:
2844 return 0;
2845}
2846
2847/* Decompose contour data inside X, Y and FLAGS, between the indices
2848 HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
2849 with DCONTEXT as an argument. Apply SCALE to each point; SCALE
2850 should be the factor necessary to turn points into 16.16 fixed
2851 point.
2852
2853 This is the version of sfnt_decompose_glyph_1 which takes
2854 sfnt_fixed (or sfnt_f26dot6) as opposed to sfnt_fword.
2855
2856 Value is 1 upon failure, else 0. */
2857
2858static int
2859sfnt_decompose_glyph_2 (size_t here, size_t last,
2860 sfnt_move_to_proc move_to,
2861 sfnt_line_to_proc line_to,
2862 sfnt_curve_to_proc curve_to,
2863 void *dcontext,
2864 sfnt_fixed *x,
2865 sfnt_fixed *y, unsigned char *flags,
2866 int scale)
2867{
2868 struct sfnt_point control1, control2, start, mid;
2869 size_t i;
2870
2871 /* The contour is empty. */
2872
2873 if (here == last)
2874 return 1;
2875
2876 /* Move the pen to the start of the contour. Apparently some fonts
2877 have off the curve points as the start of a contour, so when that
2878 happens lerp between the first and last points. */
2879
2880 if (flags[here] & 01) /* On Curve */
2881 {
2882 control1.x = x[here] * scale;
2883 control1.y = y[here] * scale;
2884 start = control1;
2885 }
2886 else if (flags[last] & 01)
2887 {
2888 /* Start at the last point if it is on the curve. Here, the
2889 start really becomes the middle of a spline. */
2890 control1.x = x[last] * scale;
2891 control1.y = y[last] * scale;
2892 start = control1;
2893
2894 /* Curve back one point early. */
2895 last -= 1;
2896 here -= 1;
2897 }
2898 else
2899 {
2900 /* Lerp between the start and the end. */
2901 control1.x = x[here] * scale;
2902 control1.y = y[here] * scale;
2903 control2.x = x[last] * scale;
2904 control2.y = y[last] * scale;
2905 sfnt_lerp_half (&control1, &control2, &start);
2906
2907 /* In either of these cases, start iterating from just here as
2908 opposed to here + 1, since logically the contour now starts
2909 from the last curve. */
2910 here -= 1;
2911 }
2912
2913 /* Move to the start. */
2914 move_to (start, dcontext);
2915
2916 /* Now handle each point between here + 1 and last. */
2917
2918 i = here;
2919 while (++i <= last)
2920 {
2921 /* If the point is on the curve, then draw a line here from the
2922 last control point. */
2923
2924 if (flags[i] & 01)
2925 {
2926 control1.x = x[i] * scale;
2927 control1.y = y[i] * scale;
2928
2929 line_to (control1, dcontext);
2930
2931 /* Move to the next point. */
2932 continue;
2933 }
2934
2935 /* Off the curve points are more interesting. They are handled
2936 one by one, with points in between being interpolated, until
2937 either the last point is reached or an on-curve point is
2938 processed. First, load the initial control points. */
2939
2940 control1.x = x[i] * scale;
2941 control1.y = y[i] * scale;
2942
2943 while (++i <= last)
2944 {
2945 /* Load this point. */
2946 control2.x = x[i] * scale;
2947 control2.y = y[i] * scale;
2948
2949 /* If this point is on the curve, curve directly to this
2950 point. */
2951
2952 if (flags[i] & 01)
2953 {
2954 curve_to (control1, control2, dcontext);
2955 goto continue_loop;
2956 }
2957
2958 /* Calculate the point between here and the previous
2959 point. */
2960 sfnt_lerp_half (&control1, &control2, &mid);
2961
2962 /* Curve over there. */
2963 curve_to (control1, mid, dcontext);
2964
2965 /* Reload the control point. */
2966 control1 = control2;
2967 }
2968
2969 /* Close the contour by curving back to start. */
2970 curve_to (control1, start, dcontext);
2971
2972 /* Don't close the contour twice. */
2973 goto exit;
2974
2975 continue_loop:
2976 continue;
2977 }
2978
2979 /* Close the contour with a line back to start. */
2980 line_to (start, dcontext);
2981
2982 exit:
2983 return 0;
2984}
2985
2711/* Decompose GLYPH into its individual components. Call MOVE_TO to 2986/* Decompose GLYPH into its individual components. Call MOVE_TO to
2712 move to a specific location. For each line encountered, call 2987 move to a specific location. For each line encountered, call
2713 LINE_TO to draw a line to that location. For each spline 2988 LINE_TO to draw a line to that location. For each spline
@@ -2736,10 +3011,8 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph,
2736 sfnt_free_glyph_proc free_glyph, 3011 sfnt_free_glyph_proc free_glyph,
2737 void *dcontext) 3012 void *dcontext)
2738{ 3013{
2739 size_t here, start, last; 3014 size_t here, last, n;
2740 struct sfnt_point pen, control1, control2;
2741 struct sfnt_compound_glyph_context context; 3015 struct sfnt_compound_glyph_context context;
2742 size_t n;
2743 3016
2744 if (glyph->simple) 3017 if (glyph->simple)
2745 { 3018 {
@@ -2756,113 +3029,23 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph,
2756 of the last point in the contour. */ 3029 of the last point in the contour. */
2757 last = glyph->simple->end_pts_of_contours[n]; 3030 last = glyph->simple->end_pts_of_contours[n];
2758 3031
2759 /* Move to the start. */ 3032 /* Make sure here and last make sense. */
2760 pen.x = glyph->simple->x_coordinates[here] * 65536;
2761 pen.y = glyph->simple->y_coordinates[here] * 65536;
2762 move_to (pen, dcontext);
2763
2764 /* Record start so the contour can be closed. */
2765 start = here;
2766 3033
2767 /* If there is only one point in a contour, draw a one pixel 3034 if (here > last || last >= glyph->simple->number_of_points)
2768 wide line. */
2769 if (last == here)
2770 {
2771 line_to (pen, dcontext);
2772 here++;
2773
2774 continue;
2775 }
2776
2777 if (here > last)
2778 /* Indices moved backwards. */
2779 return 1; 3035 return 1;
2780 3036
2781 /* Now start reading points. If the next point is on the 3037 /* Now perform the decomposition. */
2782 curve, then it is actually a line. */ 3038 if (sfnt_decompose_glyph_1 (here, last, move_to,
2783 for (++here; here <= last; ++here) 3039 line_to, curve_to,
2784 { 3040 dcontext,
2785 /* Make sure here is within bounds. */ 3041 glyph->simple->x_coordinates,
2786 if (here >= glyph->simple->number_of_points) 3042 glyph->simple->y_coordinates,
2787 return 1; 3043 glyph->simple->flags,
2788 3044 65536))
2789 if (glyph->simple->flags[here] & 01) /* On Curve */ 3045 return 1;
2790 {
2791 pen.x = glyph->simple->x_coordinates[here] * 65536;
2792 pen.y = glyph->simple->y_coordinates[here] * 65536;
2793
2794 /* See if the last point was on the curve. If it
2795 wasn't, then curve from there to here. */
2796 if (!(glyph->simple->flags[here - 1] & 01))
2797 {
2798 control1.x
2799 = glyph->simple->x_coordinates[here - 1] * 65536;
2800 control1.y
2801 = glyph->simple->y_coordinates[here - 1] * 65536;
2802 curve_to (control1, pen, dcontext);
2803 }
2804 else
2805 /* Otherwise, this is an ordinary line from there
2806 to here. */
2807 line_to (pen, dcontext);
2808
2809 continue;
2810 }
2811
2812 /* If the last point was on the curve, then there's
2813 nothing extraordinary to do yet. */
2814 if (glyph->simple->flags[here - 1] & 01)
2815 ;
2816 else
2817 {
2818 /* Otherwise, interpolate the point halfway between
2819 the last and current points and make that point
2820 the pen. */
2821 control1.x = glyph->simple->x_coordinates[here - 1] * 65536;
2822 control1.y = glyph->simple->y_coordinates[here - 1] * 65536;
2823 control2.x = glyph->simple->x_coordinates[here] * 65536;
2824 control2.y = glyph->simple->y_coordinates[here] * 65536;
2825 sfnt_lerp_half (&control1, &control2, &pen);
2826 curve_to (control1, pen, dcontext);
2827 }
2828 }
2829
2830 /* Now close the contour if there is more than one point
2831 inside it. */
2832 if (start != here - 1)
2833 {
2834 /* Restore here after the for loop increased it. */
2835 here --;
2836
2837 /* Previously, this would check whether or not start is
2838 an ``on curve'' point, but that is not necessary.
2839
2840 If a contour is not closed and the edge building
2841 process skips the second to last vertex, then the
2842 outline can end up with missing edges. */
2843
2844 pen.x = glyph->simple->x_coordinates[start] * 65536;
2845 pen.y = glyph->simple->y_coordinates[start] * 65536;
2846
2847 /* See if the last point (in this case, `here') was
2848 on the curve. If it wasn't, then curve from
2849 there to here. */
2850 if (!(glyph->simple->flags[here] & 01))
2851 {
2852 control1.x
2853 = glyph->simple->x_coordinates[here] * 65536;
2854 control1.y
2855 = glyph->simple->y_coordinates[here] * 65536;
2856 curve_to (control1, pen, dcontext);
2857 }
2858 else
2859 /* Otherwise, this is an ordinary line from there
2860 to here. */
2861 line_to (pen, dcontext);
2862 3046
2863 /* Restore here to where it was earlier. */ 3047 /* Move forward to the start of the next contour. */
2864 here++; 3048 here = last + 1;
2865 }
2866 } 3049 }
2867 3050
2868 return 0; 3051 return 0;
@@ -2898,108 +3081,22 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph,
2898 of the last point in the contour. */ 3081 of the last point in the contour. */
2899 last = context.contour_end_points[n]; 3082 last = context.contour_end_points[n];
2900 3083
2901 /* Move to the start. */ 3084 /* Make sure here and last make sense. */
2902 pen.x = context.x_coordinates[here];
2903 pen.y = context.y_coordinates[here];
2904 move_to (pen, dcontext);
2905
2906 /* Record start so the contour can be closed. */
2907 start = here;
2908
2909 /* If there is only one point in a contour, draw a one pixel
2910 wide line. */
2911 if (last == here)
2912 {
2913 line_to (pen, dcontext);
2914 here++;
2915
2916 continue;
2917 }
2918 3085
2919 if (here > last) 3086 if (here > last || last >= context.num_points)
2920 /* Indices moved backwards. */
2921 goto fail; 3087 goto fail;
2922 3088
2923 /* Now start reading points. If the next point is on the 3089 /* Now perform the decomposition. */
2924 curve, then it is actually a line. */ 3090 if (sfnt_decompose_glyph_2 (here, last, move_to,
2925 for (++here; here <= last; ++here) 3091 line_to, curve_to,
2926 { 3092 dcontext,
2927 /* Make sure here is within bounds. */ 3093 context.x_coordinates,
2928 if (here >= context.num_points) 3094 context.y_coordinates,
2929 return 1; 3095 context.flags, 1))
2930 3096 goto fail;
2931 if (context.flags[here] & 01) /* On Curve */
2932 {
2933 pen.x = context.x_coordinates[here];
2934 pen.y = context.y_coordinates[here];
2935
2936 /* See if the last point was on the curve. If it
2937 wasn't, then curve from there to here. */
2938 if (!(context.flags[here - 1] & 01))
2939 {
2940 control1.x = context.x_coordinates[here - 1];
2941 control1.y = context.y_coordinates[here - 1];
2942 curve_to (control1, pen, dcontext);
2943 }
2944 else
2945 /* Otherwise, this is an ordinary line from there
2946 to here. */
2947 line_to (pen, dcontext);
2948
2949 continue;
2950 }
2951
2952 /* If the last point was on the curve, then there's
2953 nothing extraordinary to do yet. */
2954 if (context.flags[here - 1] & 01)
2955 ;
2956 else
2957 {
2958 /* Otherwise, interpolate the point halfway between
2959 the last and current points and make that point
2960 the pen. */
2961 control1.x = context.x_coordinates[here - 1];
2962 control1.y = context.y_coordinates[here - 1];
2963 control2.x = context.x_coordinates[here];
2964 control2.y = context.y_coordinates[here];
2965 sfnt_lerp_half (&control1, &control2, &pen);
2966 curve_to (control1, pen, dcontext);
2967 }
2968 }
2969
2970 /* Now close the contour if there is more than one point
2971 inside it. */
2972 if (start != here - 1)
2973 {
2974 /* Restore here after the for loop increased it. */
2975 here --;
2976
2977 /* Previously, this would check whether or not start is an
2978 ``on curve'' point, but that is not necessary.
2979
2980 If a contour is not closed and the edge building process
2981 skips the second to last vertex, then the outline can end
2982 up with missing edges. */
2983
2984 pen.x = context.x_coordinates[start];
2985 pen.y = context.y_coordinates[start];
2986
2987 /* See if the last point (in this case, `here') was on the
2988 curve. If it wasn't, then curve from there to here. */
2989 if (!(context.flags[here] & 01))
2990 {
2991 control1.x = context.x_coordinates[here];
2992 control1.y = context.y_coordinates[here];
2993 curve_to (control1, pen, dcontext);
2994 }
2995 else
2996 /* Otherwise, this is an ordinary line from there
2997 to here. */
2998 line_to (pen, dcontext);
2999 3097
3000 /* Restore here to where it was earlier. */ 3098 /* Move forward. */
3001 here++; 3099 here = last + 1;
3002 }
3003 } 3100 }
3004 3101
3005 early: 3102 early:
@@ -10499,9 +10596,7 @@ sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline,
10499 sfnt_curve_to_proc curve_to, 10596 sfnt_curve_to_proc curve_to,
10500 void *dcontext) 10597 void *dcontext)
10501{ 10598{
10502 size_t here, start, last; 10599 size_t here, last, n;
10503 struct sfnt_point pen, control1, control2;
10504 size_t n;
10505 10600
10506 if (!outline->num_contours) 10601 if (!outline->num_contours)
10507 return 0; 10602 return 0;
@@ -10515,109 +10610,20 @@ sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline,
10515 of the last point in the contour. */ 10610 of the last point in the contour. */
10516 last = outline->contour_end_points[n]; 10611 last = outline->contour_end_points[n];
10517 10612
10518 /* Move to the start. */ 10613 /* Make sure here and last make sense. */
10519 pen.x = outline->x_points[here] * 1024;
10520 pen.y = outline->y_points[here] * 1024;
10521 move_to (pen, dcontext);
10522
10523 /* Record start so the contour can be closed. */
10524 start = here;
10525
10526 /* If there is only one point in a contour, draw a one pixel
10527 wide line. */
10528 if (last == here)
10529 {
10530 line_to (pen, dcontext);
10531 here++;
10532
10533 continue;
10534 }
10535 10614
10536 if (here > last) 10615 if (here > last || last >= outline->num_points)
10537 /* Indices moved backwards. */
10538 goto fail; 10616 goto fail;
10539 10617
10540 /* Now start reading points. If the next point is on the 10618 if (sfnt_decompose_glyph_2 (here, last, move_to,
10541 curve, then it is actually a line. */ 10619 line_to, curve_to, dcontext,
10542 for (++here; here <= last; ++here) 10620 outline->x_points,
10543 { 10621 outline->y_points,
10544 /* Make sure here is within bounds. */ 10622 outline->flags, 1024))
10545 if (here >= outline->num_points) 10623 goto fail;
10546 return 1;
10547
10548 if (outline->flags[here] & 01) /* On Curve */
10549 {
10550 pen.x = outline->x_points[here] * 1024;
10551 pen.y = outline->y_points[here] * 1024;
10552
10553 /* See if the last point was on the curve. If it
10554 wasn't, then curve from there to here. */
10555 if (!(outline->flags[here - 1] & 01))
10556 {
10557 control1.x = outline->x_points[here - 1] * 1024;
10558 control1.y = outline->y_points[here - 1] * 1024;
10559 curve_to (control1, pen, dcontext);
10560 }
10561 else
10562 /* Otherwise, this is an ordinary line from there
10563 to here. */
10564 line_to (pen, dcontext);
10565
10566 continue;
10567 }
10568
10569 /* If the last point was on the curve, then there's
10570 nothing extraordinary to do yet. */
10571 if (outline->flags[here - 1] & 01)
10572 ;
10573 else
10574 {
10575 /* Otherwise, interpolate the point halfway between
10576 the last and current points and make that point
10577 the pen. */
10578 control1.x = outline->x_points[here - 1] * 1024;
10579 control1.y = outline->y_points[here - 1] * 1024;
10580 control2.x = outline->x_points[here] * 1024;
10581 control2.y = outline->y_points[here] * 1024;
10582 sfnt_lerp_half (&control1, &control2, &pen);
10583 curve_to (control1, pen, dcontext);
10584 }
10585 }
10586
10587 /* Now close the contour if there is more than one point
10588 inside it. */
10589 if (start != here - 1)
10590 {
10591 /* Restore here after the for loop increased it. */
10592 here --;
10593
10594 /* Previously, this would check whether or not start is an
10595 ``on curve'' point, but that is not necessary.
10596
10597 If a contour is not closed and the edge building process
10598 skips the second to last vertex, then the outline can end
10599 up with missing edges. */
10600
10601 pen.x = outline->x_points[start] * 1024;
10602 pen.y = outline->y_points[start] * 1024;
10603
10604 /* See if the last point (in this case, `here') was
10605 on the curve. If it wasn't, then curve from
10606 there to here. */
10607 if (!(outline->flags[here] & 01))
10608 {
10609 control1.x = outline->x_points[here] * 1024;
10610 control1.y = outline->y_points[here] * 1024;
10611 curve_to (control1, pen, dcontext);
10612 }
10613 else
10614 /* Otherwise, this is an ordinary line from there
10615 to here. */
10616 line_to (pen, dcontext);
10617 10624
10618 /* Restore here to where it was earlier. */ 10625 /* Move forward to the start of the next contour. */
10619 here++; 10626 here = last + 1;
10620 }
10621 10627
10622 /* here may be a phantom point when outlining a compound glyph, 10628 /* here may be a phantom point when outlining a compound glyph,
10623 as they can have phantom points mixed in with contours. 10629 as they can have phantom points mixed in with contours.
@@ -15530,6 +15536,9 @@ main (int argc, char **argv)
15530 return 1; 15536 return 1;
15531 } 15537 }
15532 15538
15539 fprintf (stderr, "number of subtables: %"PRIu16"\n",
15540 table->num_subtables);
15541
15533 for (i = 0; i < table->num_subtables; ++i) 15542 for (i = 0; i < table->num_subtables; ++i)
15534 { 15543 {
15535 fprintf (stderr, "Found cmap table %"PRIu32": %p\n", 15544 fprintf (stderr, "Found cmap table %"PRIu32": %p\n",
@@ -15540,8 +15549,8 @@ main (int argc, char **argv)
15540 data[i]->format); 15549 data[i]->format);
15541 } 15550 }
15542 15551
15543#define FANCY_PPEM 12 15552#define FANCY_PPEM 40
15544#define EASY_PPEM 12 15553#define EASY_PPEM 40
15545 15554
15546 interpreter = NULL; 15555 interpreter = NULL;
15547 head = sfnt_read_head_table (fd, font); 15556 head = sfnt_read_head_table (fd, font);
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
index feea92827d9..c28a911bfba 100644
--- a/src/sfntfont-android.c
+++ b/src/sfntfont-android.c
@@ -49,9 +49,11 @@ struct sfntfont_android_scanline_buffer
49}; 49};
50 50
51/* Array of directories to search for system fonts. */ 51/* Array of directories to search for system fonts. */
52const char *system_font_directories[] = 52static char *system_font_directories[] =
53 { 53 {
54 "/system/fonts", 54 "/system/fonts",
55 /* This should be filled in by init_sfntfont_android. */
56 (char[PATH_MAX]) { },
55 }; 57 };
56 58
57/* The font cache. */ 59/* The font cache. */
@@ -691,6 +693,10 @@ loaded before character sets are made available. */)
691 { 693 {
692 dir = opendir (system_font_directories[i]); 694 dir = opendir (system_font_directories[i]);
693 695
696 __android_log_print (ANDROID_LOG_VERBOSE, __func__,
697 "Loading fonts from: %s",
698 system_font_directories[i]);
699
694 if (!dir) 700 if (!dir)
695 continue; 701 continue;
696 702
@@ -752,6 +758,11 @@ init_sfntfont_android (void)
752 build_string ("Droid Sans Mono")), 758 build_string ("Droid Sans Mono")),
753 Fcons (build_string ("Sans Serif"), 759 Fcons (build_string ("Sans Serif"),
754 build_string ("Droid Sans"))); 760 build_string ("Droid Sans")));
761
762 /* Set up the user fonts directory. This directory is ``fonts'' in
763 the Emacs files directory. */
764 snprintf (system_font_directories[1], PATH_MAX, "%s/fonts",
765 android_get_home_directory ());
755} 766}
756 767
757void 768void
diff --git a/src/sfntfont.c b/src/sfntfont.c
index a5ed54394a2..f9344067f1a 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -87,6 +87,10 @@ struct sfnt_font_desc
87 87
88 /* The offset of the table directory within PATH. */ 88 /* The offset of the table directory within PATH. */
89 off_t offset; 89 off_t offset;
90
91 /* The number of glyphs in this font. Used to catch invalid cmap
92 tables. This is actually the number of glyphs - 1. */
93 int num_glyphs;
90}; 94};
91 95
92/* List of fonts. */ 96/* List of fonts. */
@@ -517,10 +521,11 @@ sfnt_enum_font_1 (int fd, const char *file,
517 struct sfnt_offset_subtable *subtables, 521 struct sfnt_offset_subtable *subtables,
518 off_t offset) 522 off_t offset)
519{ 523{
520 struct sfnt_font_desc *desc; 524 struct sfnt_font_desc *desc, **next, *prev;
521 struct sfnt_head_table *head; 525 struct sfnt_head_table *head;
522 struct sfnt_name_table *name; 526 struct sfnt_name_table *name;
523 struct sfnt_meta_table *meta; 527 struct sfnt_meta_table *meta;
528 struct sfnt_maxp_table *maxp;
524 Lisp_Object family, style; 529 Lisp_Object family, style;
525 530
526 /* Create the font desc and copy in the file name. */ 531 /* Create the font desc and copy in the file name. */
@@ -543,12 +548,16 @@ sfnt_enum_font_1 (int fd, const char *file,
543 if (!name) 548 if (!name)
544 goto bail2; 549 goto bail2;
545 550
551 maxp = sfnt_read_maxp_table (fd, subtables);
552 if (!maxp)
553 goto bail3;
554
546 /* meta is not required, nor present on many non-Apple fonts. */ 555 /* meta is not required, nor present on many non-Apple fonts. */
547 meta = sfnt_read_meta_table (fd, subtables); 556 meta = sfnt_read_meta_table (fd, subtables);
548 557
549 /* Decode the family and style from the name table. */ 558 /* Decode the family and style from the name table. */
550 if (sfnt_decode_family_style (name, &family, &style)) 559 if (sfnt_decode_family_style (name, &family, &style))
551 goto bail3; 560 goto bail4;
552 561
553 /* Set the family. */ 562 /* Set the family. */
554 desc->family = family; 563 desc->family = family;
@@ -556,6 +565,9 @@ sfnt_enum_font_1 (int fd, const char *file,
556 desc->char_cache = Qnil; 565 desc->char_cache = Qnil;
557 desc->subtable.platform_id = 500; 566 desc->subtable.platform_id = 500;
558 567
568 /* Set the largest glyph identifier. */
569 desc->num_glyphs = maxp->num_glyphs;
570
559 /* Parse the style. */ 571 /* Parse the style. */
560 sfnt_parse_style (style, desc); 572 sfnt_parse_style (style, desc);
561 573
@@ -584,13 +596,32 @@ sfnt_enum_font_1 (int fd, const char *file,
584 desc->next = system_fonts; 596 desc->next = system_fonts;
585 system_fonts = desc; 597 system_fonts = desc;
586 598
599 /* Remove any fonts which have the same style as this one. */
600
601 next = &system_fonts->next;
602 prev = *next;
603 for (; *next; prev = *next)
604 {
605 if (!NILP (Fstring_equal (prev->style, desc->style))
606 && !NILP (Fstring_equal (prev->family, desc->family)))
607 {
608 *next = prev->next;
609 xfree (prev);
610 }
611 else
612 next = &prev->next;
613 }
614
587 xfree (meta); 615 xfree (meta);
616 xfree (maxp);
588 xfree (name); 617 xfree (name);
589 xfree (head); 618 xfree (head);
590 return 0; 619 return 0;
591 620
592 bail3: 621 bail4:
593 xfree (meta); 622 xfree (meta);
623 xfree (maxp);
624 bail3:
594 xfree (name); 625 xfree (name);
595 bail2: 626 bail2:
596 xfree (head); 627 xfree (head);
@@ -602,6 +633,8 @@ sfnt_enum_font_1 (int fd, const char *file,
602/* Enumerate the font FILE into the list of system fonts. Return 1 if 633/* Enumerate the font FILE into the list of system fonts. Return 1 if
603 it could not be enumerated, 0 otherwise. 634 it could not be enumerated, 0 otherwise.
604 635
636 Remove any font whose family and style is a duplicate of this one.
637
605 FILE can either be a TrueType collection file containing TrueType 638 FILE can either be a TrueType collection file containing TrueType
606 fonts, or a TrueType font itself. */ 639 fonts, or a TrueType font itself. */
607 640
@@ -960,6 +993,25 @@ sfntfont_read_cmap (struct sfnt_font_desc *desc,
960 emacs_close (fd); 993 emacs_close (fd);
961} 994}
962 995
996/* Return whether or not CHARACTER has an associated mapping in CMAP,
997 and the mapping points to a valid glyph. DESC is the font
998 descriptor associated with the font. */
999
1000static bool
1001sfntfont_glyph_valid (struct sfnt_font_desc *desc,
1002 sfnt_char font_character,
1003 struct sfnt_cmap_encoding_subtable_data *cmap)
1004{
1005 sfnt_glyph glyph;
1006
1007 glyph = sfnt_lookup_glyph (font_character, cmap);
1008
1009 if (!glyph)
1010 return false;
1011
1012 return glyph <= desc->num_glyphs;
1013}
1014
963/* Look up a character CHARACTER in the font description DESC. Cache 1015/* Look up a character CHARACTER in the font description DESC. Cache
964 the results. Return true if the character exists, false otherwise. 1016 the results. Return true if the character exists, false otherwise.
965 1017
@@ -1013,8 +1065,10 @@ sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character,
1013 if (font_character == CHARSET_INVALID_CODE (charset)) 1065 if (font_character == CHARSET_INVALID_CODE (charset))
1014 return false; 1066 return false;
1015 1067
1016 /* Now return whether or not the glyph is present. */ 1068 /* Now return whether or not the glyph is present. Noto Sans
1017 present = sfnt_lookup_glyph (font_character, *cmap) != 0; 1069 Georgian comes with a corrupt format 4 cmap table that somehow
1070 tries to express glyphs greater than 65565. */
1071 present = sfntfont_glyph_valid (desc, font_character, *cmap);
1018 1072
1019 /* Cache the result. Store Qlambda when not present, Qt 1073 /* Cache the result. Store Qlambda when not present, Qt
1020 otherwise. */ 1074 otherwise. */
@@ -1133,22 +1187,23 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec)
1133 { 1187 {
1134 tem = XCDR (tem); 1188 tem = XCDR (tem);
1135 1189
1136 /* tem is a list of each characters, one of which must be 1190 /* tem is a list of each characters, all of which must be
1137 present in the font. */ 1191 present in the font. */
1138 FOR_EACH_TAIL_SAFE (tem) 1192 FOR_EACH_TAIL_SAFE (tem)
1139 { 1193 {
1140 if (FIXNUMP (XCAR (tem))) 1194 if (FIXNUMP (XCAR (tem))
1141 { 1195 && !sfntfont_lookup_char (desc, XCAR (tem), &cmap,
1142 if (!sfntfont_lookup_char (desc, XCAR (tem), &cmap, 1196 &subtable))
1143 &subtable)) 1197 goto fail;
1144 goto fail;
1145
1146 /* One character is enough to pass a font. Don't
1147 look at too many. */
1148 break;
1149 }
1150 } 1198 }
1199
1200 /* One or more characters are missing. */
1201 if (!NILP (tem))
1202 goto fail;
1151 } 1203 }
1204 /* Fail if there are no matching fonts at all. */
1205 else if (NILP (tem))
1206 goto fail;
1152 } 1207 }
1153 1208
1154 /* Now check that the language is supported. */ 1209 /* Now check that the language is supported. */
diff --git a/src/textconv.c b/src/textconv.c
index 835d03f3037..5090b0a33b6 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -35,6 +35,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
35#include "textconv.h" 35#include "textconv.h"
36#include "buffer.h" 36#include "buffer.h"
37#include "syntax.h" 37#include "syntax.h"
38#include "blockinput.h"
38 39
39 40
40 41
@@ -47,6 +48,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
47 48
48static struct textconv_interface *text_interface; 49static struct textconv_interface *text_interface;
49 50
51/* How many times text conversion has been disabled. */
52
53static int suppress_conversion_count;
54
50/* Flags used to determine what must be sent after a batch edit 55/* Flags used to determine what must be sent after a batch edit
51 ends. */ 56 ends. */
52 57
@@ -391,7 +396,8 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query,
391static void 396static void
392sync_overlay (struct frame *f) 397sync_overlay (struct frame *f)
393{ 398{
394 if (MARKERP (f->conversion.compose_region_start)) 399 if (MARKERP (f->conversion.compose_region_start)
400 && !NILP (Vtext_conversion_face))
395 { 401 {
396 if (NILP (f->conversion.compose_region_overlay)) 402 if (NILP (f->conversion.compose_region_overlay))
397 { 403 {
@@ -400,7 +406,7 @@ sync_overlay (struct frame *f)
400 f->conversion.compose_region_end, Qnil, 406 f->conversion.compose_region_end, Qnil,
401 Qt, Qnil); 407 Qt, Qnil);
402 Foverlay_put (f->conversion.compose_region_overlay, 408 Foverlay_put (f->conversion.compose_region_overlay,
403 Qface, Qunderline); 409 Qface, Vtext_conversion_face);
404 } 410 }
405 411
406 Fmove_overlay (f->conversion.compose_region_overlay, 412 Fmove_overlay (f->conversion.compose_region_overlay,
@@ -514,6 +520,7 @@ really_commit_text (struct frame *f, EMACS_INT position,
514{ 520{
515 specpdl_ref count; 521 specpdl_ref count;
516 ptrdiff_t wanted, start, end; 522 ptrdiff_t wanted, start, end;
523 struct window *w;
517 524
518 /* If F's old selected window is no longer live, fail. */ 525 /* If F's old selected window is no longer live, fail. */
519 526
@@ -624,6 +631,10 @@ really_commit_text (struct frame *f, EMACS_INT position,
624 631
625 /* This should deactivate the mark. */ 632 /* This should deactivate the mark. */
626 call0 (Qdeactivate_mark); 633 call0 (Qdeactivate_mark);
634
635 /* Update the ephemeral last point. */
636 w = XWINDOW (selected_window);
637 w->ephemeral_last_point = PT;
627 unbind_to (count, Qnil); 638 unbind_to (count, Qnil);
628} 639}
629 640
@@ -760,6 +771,10 @@ really_set_composing_text (struct frame *f, ptrdiff_t position,
760 text_interface->compose_region_changed (f); 771 text_interface->compose_region_changed (f);
761 } 772 }
762 773
774 /* Update the ephemeral last point. */
775 w = XWINDOW (selected_window);
776 w->ephemeral_last_point = PT;
777
763 unbind_to (count, Qnil); 778 unbind_to (count, Qnil);
764} 779}
765 780
@@ -771,6 +786,7 @@ really_set_composing_region (struct frame *f, ptrdiff_t start,
771 ptrdiff_t end) 786 ptrdiff_t end)
772{ 787{
773 specpdl_ref count; 788 specpdl_ref count;
789 struct window *w;
774 790
775 /* If F's old selected window is no longer live, fail. */ 791 /* If F's old selected window is no longer live, fail. */
776 792
@@ -810,6 +826,10 @@ really_set_composing_region (struct frame *f, ptrdiff_t start,
810 make_fixnum (end), Qnil); 826 make_fixnum (end), Qnil);
811 sync_overlay (f); 827 sync_overlay (f);
812 828
829 /* Update the ephemeral last point. */
830 w = XWINDOW (selected_window);
831 w->ephemeral_last_point = PT;
832
813 unbind_to (count, Qnil); 833 unbind_to (count, Qnil);
814} 834}
815 835
@@ -823,6 +843,7 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
823{ 843{
824 specpdl_ref count; 844 specpdl_ref count;
825 ptrdiff_t start, end, a, b, a1, b1, lstart, rstart; 845 ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
846 struct window *w;
826 847
827 /* If F's old selected window is no longer live, fail. */ 848 /* If F's old selected window is no longer live, fail. */
828 849
@@ -889,6 +910,10 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
889 if (get_mark () == PT) 910 if (get_mark () == PT)
890 call0 (Qdeactivate_mark); 911 call0 (Qdeactivate_mark);
891 912
913 /* Update the ephemeral last point. */
914 w = XWINDOW (selected_window);
915 w->ephemeral_last_point = PT;
916
892 unbind_to (count, Qnil); 917 unbind_to (count, Qnil);
893} 918}
894 919
@@ -904,6 +929,7 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point,
904 ptrdiff_t mark) 929 ptrdiff_t mark)
905{ 930{
906 specpdl_ref count; 931 specpdl_ref count;
932 struct window *w;
907 933
908 /* If F's old selected window is no longer live, fail. */ 934 /* If F's old selected window is no longer live, fail. */
909 935
@@ -922,7 +948,7 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point,
922 { 948 {
923 if (f->conversion.batch_edit_count > 0) 949 if (f->conversion.batch_edit_count > 0)
924 f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE; 950 f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
925 else 951 else if (text_interface && text_interface->point_changed)
926 text_interface->point_changed (f, 952 text_interface->point_changed (f,
927 XWINDOW (f->old_selected_window), 953 XWINDOW (f->old_selected_window),
928 current_buffer); 954 current_buffer);
@@ -936,6 +962,10 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point,
936 else 962 else
937 call1 (Qpush_mark, make_fixnum (mark)); 963 call1 (Qpush_mark, make_fixnum (mark));
938 964
965 /* Update the ephemeral last point. */
966 w = XWINDOW (selected_window);
967 w->ephemeral_last_point = PT;
968
939 unbind_to (count, Qnil); 969 unbind_to (count, Qnil);
940} 970}
941 971
@@ -949,9 +979,11 @@ complete_edit (void *token)
949} 979}
950 980
951/* Process and free the text conversion ACTION. F must be the frame 981/* Process and free the text conversion ACTION. F must be the frame
952 on which ACTION will be performed. */ 982 on which ACTION will be performed.
953 983
954static void 984 Value is the window which was used, or NULL. */
985
986static struct window *
955handle_pending_conversion_events_1 (struct frame *f, 987handle_pending_conversion_events_1 (struct frame *f,
956 struct text_conversion_action *action) 988 struct text_conversion_action *action)
957{ 989{
@@ -969,9 +1001,23 @@ handle_pending_conversion_events_1 (struct frame *f,
969 token = action->counter; 1001 token = action->counter;
970 xfree (action); 1002 xfree (action);
971 1003
1004 /* Text conversion events can still arrive immediately after
1005 `conversion_disabled_p' becomes true. In that case, process all
1006 events, but don't perform any associated actions. */
1007
1008 if (conversion_disabled_p ())
1009 return NULL;
1010
972 /* Make sure completion is signalled. */ 1011 /* Make sure completion is signalled. */
973 count = SPECPDL_INDEX (); 1012 count = SPECPDL_INDEX ();
974 record_unwind_protect_ptr (complete_edit, &token); 1013 record_unwind_protect_ptr (complete_edit, &token);
1014 w = NULL;
1015
1016 if (WINDOW_LIVE_P (f->old_selected_window))
1017 {
1018 w = XWINDOW (f->old_selected_window);
1019 buffer = XBUFFER (WINDOW_BUFFER (w));
1020 }
975 1021
976 switch (operation) 1022 switch (operation)
977 { 1023 {
@@ -987,12 +1033,7 @@ handle_pending_conversion_events_1 (struct frame *f,
987 break; 1033 break;
988 1034
989 if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE) 1035 if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE)
990 { 1036 text_interface->point_changed (f, w, buffer);
991 w = XWINDOW (f->old_selected_window);
992 buffer = XBUFFER (WINDOW_BUFFER (w));
993
994 text_interface->point_changed (f, w, buffer);
995 }
996 1037
997 if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE) 1038 if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE)
998 text_interface->compose_region_changed (f); 1039 text_interface->compose_region_changed (f);
@@ -1030,6 +1071,8 @@ handle_pending_conversion_events_1 (struct frame *f,
1030 } 1071 }
1031 1072
1032 unbind_to (count, Qnil); 1073 unbind_to (count, Qnil);
1074
1075 return w;
1033} 1076}
1034 1077
1035/* Decrement the variable pointed to by *PTR. */ 1078/* Decrement the variable pointed to by *PTR. */
@@ -1055,6 +1098,8 @@ handle_pending_conversion_events (void)
1055 bool handled; 1098 bool handled;
1056 static int inside; 1099 static int inside;
1057 specpdl_ref count; 1100 specpdl_ref count;
1101 ptrdiff_t last_point;
1102 struct window *w;
1058 1103
1059 handled = false; 1104 handled = false;
1060 1105
@@ -1065,6 +1110,8 @@ handle_pending_conversion_events (void)
1065 Vtext_conversion_edits = Qnil; 1110 Vtext_conversion_edits = Qnil;
1066 1111
1067 inside++; 1112 inside++;
1113 last_point = -1;
1114 w = NULL;
1068 1115
1069 count = SPECPDL_INDEX (); 1116 count = SPECPDL_INDEX ();
1070 record_unwind_protect_ptr (decrement_inside, &inside); 1117 record_unwind_protect_ptr (decrement_inside, &inside);
@@ -1077,16 +1124,26 @@ handle_pending_conversion_events (void)
1077 process them in bottom to up order. */ 1124 process them in bottom to up order. */
1078 while (true) 1125 while (true)
1079 { 1126 {
1080 /* Redisplay in between if there is more than one 1127 /* Update the input method if handled &&
1081 action. 1128 w->ephemeral_last_point != last_point. */
1082 1129 if (w && (last_point != w->ephemeral_last_point))
1083 This can read input. This function must be reentrant 1130 {
1084 here. */ 1131 if (handled
1085 1132 && last_point != -1
1086 if (handled) 1133 && text_interface
1087 redisplay (); 1134 && text_interface->point_changed)
1135 {
1136 if (f->conversion.batch_edit_count > 0)
1137 f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
1138 else
1139 text_interface->point_changed (f, NULL, NULL);
1140 }
1141
1142 last_point = w->ephemeral_last_point;
1143 }
1088 1144
1089 /* Reload action. */ 1145 /* Reload action. This needs to be reentrant as buffer
1146 modification functions can call `read-char'. */
1090 action = f->conversion.actions; 1147 action = f->conversion.actions;
1091 1148
1092 /* If there are no more actions, break. */ 1149 /* If there are no more actions, break. */
@@ -1099,7 +1156,7 @@ handle_pending_conversion_events (void)
1099 f->conversion.actions = next; 1156 f->conversion.actions = next;
1100 1157
1101 /* Handle and free the action. */ 1158 /* Handle and free the action. */
1102 handle_pending_conversion_events_1 (f, action); 1159 w = handle_pending_conversion_events_1 (f, action);
1103 handled = true; 1160 handled = true;
1104 } 1161 }
1105 } 1162 }
@@ -1399,6 +1456,16 @@ get_extracted_text (struct frame *f, ptrdiff_t n,
1399 return buffer; 1456 return buffer;
1400} 1457}
1401 1458
1459/* Return whether or not text conversion is temporarily disabled.
1460 `reset' should always call this to determine whether or not to
1461 disable the input method. */
1462
1463bool
1464conversion_disabled_p (void)
1465{
1466 return suppress_conversion_count > 0;
1467}
1468
1402 1469
1403 1470
1404/* Window system interface. These are called from the rest of 1471/* Window system interface. These are called from the rest of
@@ -1440,6 +1507,60 @@ report_point_change (struct frame *f, struct window *window,
1440 text_interface->point_changed (f, window, buffer); 1507 text_interface->point_changed (f, window, buffer);
1441} 1508}
1442 1509
1510/* Temporarily disable text conversion. Must be paired with a
1511 corresponding call to resume_text_conversion. */
1512
1513void
1514disable_text_conversion (void)
1515{
1516 Lisp_Object tail, frame;
1517 struct frame *f;
1518
1519 suppress_conversion_count++;
1520
1521 if (!text_interface || suppress_conversion_count > 1)
1522 return;
1523
1524 /* Loop through and reset the input method on each window system
1525 frame. It should call conversion_disabled_p and then DTRT. */
1526
1527 FOR_EACH_FRAME (tail, frame)
1528 {
1529 f = XFRAME (frame);
1530 reset_frame_state (f);
1531
1532 if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
1533 text_interface->reset (f);
1534 }
1535}
1536
1537/* Undo the effect of the last call to `disable_text_conversion'. */
1538
1539void
1540resume_text_conversion (void)
1541{
1542 Lisp_Object tail, frame;
1543 struct frame *f;
1544
1545 suppress_conversion_count--;
1546 eassert (suppress_conversion_count >= 0);
1547
1548 if (!text_interface || suppress_conversion_count)
1549 return;
1550
1551 /* Loop through and reset the input method on each window system
1552 frame. It should call conversion_disabled_p and then DTRT. */
1553
1554 FOR_EACH_FRAME (tail, frame)
1555 {
1556 f = XFRAME (frame);
1557 reset_frame_state (f);
1558
1559 if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
1560 text_interface->reset (f);
1561 }
1562}
1563
1443/* Register INTERFACE as the text conversion interface. */ 1564/* Register INTERFACE as the text conversion interface. */
1444 1565
1445void 1566void
@@ -1450,6 +1571,59 @@ register_textconv_interface (struct textconv_interface *interface)
1450 1571
1451 1572
1452 1573
1574/* Lisp interface. */
1575
1576DEFUN ("set-text-conversion-style", Fset_text_conversion_style,
1577 Sset_text_conversion_style, 1, 1, 0,
1578 doc: /* Set the text conversion style in the current buffer.
1579
1580Set `text-conversion-mode' to VALUE, then force any input method
1581editing frame displaying this buffer to stop itself.
1582
1583This can lead to a significant amount of time being taken by the input
1584method resetting itself, so you should not use this function lightly;
1585instead, set `text-conversion-mode' before your buffer is displayed,
1586and let redisplay manage the input method appropriately. */)
1587 (Lisp_Object value)
1588{
1589 Lisp_Object tail, frame;
1590 struct frame *f;
1591 Lisp_Object buffer;
1592
1593 bset_text_conversion_style (current_buffer, value);
1594
1595 if (!text_interface)
1596 return Qnil;
1597
1598 /* If there are any seleted windows displaying this buffer, reset
1599 text conversion on their associated frames. */
1600
1601 if (buffer_window_count (current_buffer))
1602 {
1603 buffer = Fcurrent_buffer ();
1604
1605 FOR_EACH_FRAME (tail, frame)
1606 {
1607 f = XFRAME (frame);
1608
1609 if (WINDOW_LIVE_P (f->old_selected_window)
1610 && FRAME_WINDOW_P (f)
1611 && EQ (XWINDOW (f->old_selected_window)->contents,
1612 buffer))
1613 {
1614 block_input ();
1615 reset_frame_state (f);
1616 text_interface->reset (f);
1617 unblock_input ();
1618 }
1619 }
1620 }
1621
1622 return Qnil;
1623}
1624
1625
1626
1453void 1627void
1454syms_of_textconv (void) 1628syms_of_textconv (void)
1455{ 1629{
@@ -1457,6 +1631,7 @@ syms_of_textconv (void)
1457 DEFSYM (Qtext_conversion, "text-conversion"); 1631 DEFSYM (Qtext_conversion, "text-conversion");
1458 DEFSYM (Qpush_mark, "push-mark"); 1632 DEFSYM (Qpush_mark, "push-mark");
1459 DEFSYM (Qunderline, "underline"); 1633 DEFSYM (Qunderline, "underline");
1634 DEFSYM (Qoverriding_text_conversion_style, "overriding-text-conversion-style");
1460 1635
1461 DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits, 1636 DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
1462 doc: /* List of buffers that were last edited as a result of text conversion. 1637 doc: /* List of buffers that were last edited as a result of text conversion.
@@ -1480,4 +1655,21 @@ inserted.
1480If a deletion occured, then BEG and END are the same, and EPHEMERAL is 1655If a deletion occured, then BEG and END are the same, and EPHEMERAL is
1481nil. */); 1656nil. */);
1482 Vtext_conversion_edits = Qnil; 1657 Vtext_conversion_edits = Qnil;
1658
1659 DEFVAR_LISP ("overriding-text-conversion-style",
1660 Voverriding_text_conversion_style,
1661 doc: /* Non-buffer local version of `text-conversion-style'.
1662
1663If this variable is the symbol `lambda', it means to consult the
1664buffer local variable `text-conversion-style' to determine whether or
1665not to activate the input method. Otherwise, its value is used in
1666preference to any buffer local value of `text-conversion-style'. */);
1667 Voverriding_text_conversion_style = Qlambda;
1668
1669 DEFVAR_LISP ("text-conversion-face", Vtext_conversion_face,
1670 doc: /* Face in which to display temporary edits by an input method.
1671nil means to display no indication of a temporary edit. */);
1672 Vtext_conversion_face = Qunderline;
1673
1674 defsubr (&Sset_text_conversion_style);
1483} 1675}
diff --git a/src/textconv.h b/src/textconv.h
index 034c663521a..16d13deb092 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -140,6 +140,7 @@ extern void delete_surrounding_text (struct frame *, ptrdiff_t,
140 ptrdiff_t, unsigned long); 140 ptrdiff_t, unsigned long);
141extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, 141extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
142 ptrdiff_t *, ptrdiff_t *, ptrdiff_t *); 142 ptrdiff_t *, ptrdiff_t *, ptrdiff_t *);
143extern bool conversion_disabled_p (void);
143 144
144extern void register_textconv_interface (struct textconv_interface *); 145extern void register_textconv_interface (struct textconv_interface *);
145 146
diff --git a/src/window.h b/src/window.h
index 463c7f89b9b..36b1bfb7283 100644
--- a/src/window.h
+++ b/src/window.h
@@ -286,6 +286,21 @@ struct window
286 it should be positive. */ 286 it should be positive. */
287 ptrdiff_t last_point; 287 ptrdiff_t last_point;
288 288
289#ifdef HAVE_TEXT_CONVERSION
290 /* ``ephemeral'' last point position. This is used while
291 processing text conversion events.
292
293 `last_point' is normally used during redisplay to indicate the
294 position of point as seem by the input method. However, it is
295 not updated if consequtive conversions are processed at the
296 same time.
297
298 This `ephemeral_last_point' field is either the last point as
299 set in redisplay or the last point after a text editing
300 operation. */
301 ptrdiff_t ephemeral_last_point;
302#endif
303
289 /* Value of mark in the selected window at the time of the last 304 /* Value of mark in the selected window at the time of the last
290 redisplay. */ 305 redisplay. */
291 ptrdiff_t last_mark; 306 ptrdiff_t last_mark;
diff --git a/src/xdisp.c b/src/xdisp.c
index 525eaa64b3a..a17fa97ee20 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -17315,6 +17315,9 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
17315 w->last_mark = -1; 17315 w->last_mark = -1;
17316 17316
17317#ifdef HAVE_TEXT_CONVERSION 17317#ifdef HAVE_TEXT_CONVERSION
17318 /* See the description of this field in struct window. */
17319 w->ephemeral_last_point = w->last_point;
17320
17318 /* Point motion is only propagated to the input method for use 17321 /* Point motion is only propagated to the input method for use
17319 in text conversion during a redisplay. While this can lead 17322 in text conversion during a redisplay. While this can lead
17320 to inconsistencies when point has moved but the change has 17323 to inconsistencies when point has moved but the change has