diff --git a/manual/luatex.pdf b/manual/luatex.pdf
index 19182e8764b5213147d626aa4348cfc74665493f..36e6433078df5e9e70bda49e4451222a1ba7198d 100644
Binary files a/manual/luatex.pdf and b/manual/luatex.pdf differ
diff --git a/source/texk/web2c/luatexdir/lang/texlang.w b/source/texk/web2c/luatexdir/lang/texlang.w
index f000cfb5a3d0db1fdf4ae03aec68490b9adc6b70..bd519af632ab8192a136c0ce982805ebcc717be8 100644
--- a/source/texk/web2c/luatexdir/lang/texlang.w
+++ b/source/texk/web2c/luatexdir/lang/texlang.w
@@ -857,7 +857,7 @@ void hnj_hyphenation(halfword head, halfword tail)
 
     assert(tail != null);
     save_tail1 = vlink(tail);
-    s = new_penalty(0);
+    s = new_penalty(0,word_penalty);
     couple_nodes(tail, s);
 
     while (r != null) {         /* could be while(1), but let's be paranoid */
diff --git a/source/texk/web2c/luatexdir/tex/linebreak.w b/source/texk/web2c/luatexdir/tex/linebreak.w
index ad003097105490ad3db0e812c373b7fbffe630a9..48705ad8cad5783a11b630b055eb6bf4205c01f7 100644
--- a/source/texk/web2c/luatexdir/tex/linebreak.w
+++ b/source/texk/web2c/luatexdir/tex/linebreak.w
@@ -104,14 +104,14 @@ void line_break(boolean d, int line_break_context)
     new_hyphenation(temp_head, cur_list.tail_field);
     cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field);
     if (is_char_node(cur_list.tail_field)) {
-        tail_append(new_penalty(inf_penalty));
+        tail_append(new_penalty(inf_penalty,line_penalty));
     } else if (type(cur_list.tail_field) != glue_node) {
-        tail_append(new_penalty(inf_penalty));
+        tail_append(new_penalty(inf_penalty,line_penalty));
     } else {
         halfword t = alink(cur_list.tail_field);
 		flush_node(cur_list.tail_field);
 		cur_list.tail_field = t;
-		tail_append(new_penalty(inf_penalty));
+		tail_append(new_penalty(inf_penalty,line_penalty));
     }
     final_par_glue = new_param_glue(par_fill_skip_code);
     couple_nodes(cur_list.tail_field, final_par_glue);
diff --git a/source/texk/web2c/luatexdir/tex/maincontrol.w b/source/texk/web2c/luatexdir/tex/maincontrol.w
index 965334021113f577efdb080660a7926281c38eb8..0baba872e8e90622fa2d8a8f6bb165d832259936 100644
--- a/source/texk/web2c/luatexdir/tex/maincontrol.w
+++ b/source/texk/web2c/luatexdir/tex/maincontrol.w
@@ -1035,7 +1035,7 @@ boolean its_all_over(void)
         tail_append(new_null_box());
         width(tail) = hsize_par;
         tail_append(new_glue(fill_glue));
-        tail_append(new_penalty(-010000000000));
+        tail_append(new_penalty(-010000000000,final_penalty));
         normal_page_filter(end);
         build_page();           /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */
     }
@@ -1599,7 +1599,7 @@ void handle_mark(void)
 void append_penalty(void)
 {
     scan_int();
-    tail_append(new_penalty(cur_val));
+    tail_append(new_penalty(cur_val,user_penalty));
     if (mode == vmode) {
         checked_page_filter(penalty);
         build_page();
diff --git a/source/texk/web2c/luatexdir/tex/mlist.w b/source/texk/web2c/luatexdir/tex/mlist.w
index 170dd51d30ad86141411aa0e08c97dea5a4e260b..7a002c2843d1d41678afabd908245ab3665e6e06 100644
--- a/source/texk/web2c/luatexdir/tex/mlist.w
+++ b/source/texk/web2c/luatexdir/tex/mlist.w
@@ -4257,7 +4257,7 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
             r_type = type(vlink(q));
             r_subtype = subtype(vlink(q));
             if (r_type != penalty_node && (r_type != simple_noad || r_subtype != rel_noad_type)) {
-                z = new_penalty(pen);
+                z = new_penalty(pen,noad_penalty);
                 reset_attributes(z, node_attr(q));
                 couple_nodes(p,z);
                 p = z;
diff --git a/source/texk/web2c/luatexdir/tex/postlinebreak.w b/source/texk/web2c/luatexdir/tex/postlinebreak.w
index fe11d76d0b14ea27e63b2ba76087b1af196433df..dd85cf01b3efee968419e03dead1c092ad764df8 100644
--- a/source/texk/web2c/luatexdir/tex/postlinebreak.w
+++ b/source/texk/web2c/luatexdir/tex/postlinebreak.w
@@ -505,7 +505,7 @@ void ext_post_line_break(int paragraph_dir,
                 }
             }
             if (pen != 0) {
-                r = new_penalty(pen);
+                r = new_penalty(pen,linebreak_penalty);
                 couple_nodes(cur_list.tail_field, r);
                 cur_list.tail_field = r;
             }
diff --git a/source/texk/web2c/luatexdir/tex/texmath.w b/source/texk/web2c/luatexdir/tex/texmath.w
index 2f7fe90689bbaf3fa06b1e1cc8e746f7b792f31a..eeeb75bd7920413c770a234f158e8820b48f384e 100644
--- a/source/texk/web2c/luatexdir/tex/texmath.w
+++ b/source/texk/web2c/luatexdir/tex/texmath.w
@@ -2249,7 +2249,7 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
                     d = 0;
     }
 
-    tail_append(new_penalty(pre_display_penalty_par));
+    tail_append(new_penalty(pre_display_penalty_par,after_display_penalty));
     if ((d + line_s <= pre_display_size_par) || l) {        /* not enough clearance */
         g1 = above_display_skip_code;
         g2 = below_display_skip_code;
@@ -2273,7 +2273,7 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
      /* } else {                       */
      /* }                              */
         append_to_vlist(eqno_box,lua_key_index(equation_number));
-        tail_append(new_penalty(inf_penalty));
+        tail_append(new_penalty(inf_penalty,equation_number_penalty));
     } else {
         inject_display_skip_before(g1);
     }
@@ -2364,7 +2364,7 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
     append_to_vlist(eq_box,lua_key_index(equation));
 
     if ((eqno_box != null) && (eqno_w == 0) && !l) {
-        tail_append(new_penalty(inf_penalty));
+        tail_append(new_penalty(inf_penalty,equation_number_penalty));
      /* if (math_direction_par==dir_TLT) { */
             shift_amount(eqno_box) = line_s + line_w - eqno_width ;
      /* } else {                       */
@@ -2384,7 +2384,7 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
         alink(pre_adjust_tail) = alink(tail);
         tail = pre_t;
     }
-    tail_append(new_penalty(post_display_penalty_par));
+    tail_append(new_penalty(post_display_penalty_par,after_display_penalty));
     inject_display_skip_after(g2);
     resume_after_display();
 }
@@ -2536,12 +2536,12 @@ void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
         check_display_math_end();
     }
     pop_nest();
-    tail_append(new_penalty(pre_display_penalty_par));
+    tail_append(new_penalty(pre_display_penalty_par,before_display_penalty));
     inject_display_skip_before(above_display_skip_code);
     vlink(tail) = p;
     if (p != null)
         tail = q;
-    tail_append(new_penalty(post_display_penalty_par));
+    tail_append(new_penalty(post_display_penalty_par,after_display_penalty));
     inject_display_skip_after(below_display_skip_code);
     cur_list.prev_depth_field = saved_prevdepth;
     resume_after_display();
diff --git a/source/texk/web2c/luatexdir/tex/texnodes.h b/source/texk/web2c/luatexdir/tex/texnodes.h
index dda0925609e7ed9c7e02b8d552314e42fdc5922d..b838dd0a2f95570e01d5fd5575de8d8d3ae2b8be 100644
--- a/source/texk/web2c/luatexdir/tex/texnodes.h
+++ b/source/texk/web2c/luatexdir/tex/texnodes.h
@@ -127,6 +127,18 @@ typedef enum {
 #  define penalty_node_size    3
 #  define penalty(a)           vlink((a)+2)
 
+typedef enum {
+    user_penalty,
+    linebreak_penalty, /* includes widow, club, broken ect */
+    line_penalty,
+    word_penalty,
+    final_penalty,
+    noad_penalty,
+    before_display_penalty,
+    after_display_penalty,
+    equation_number_penalty,
+} penalty_subtypes ;
+
 /*
 #  define glue_node_size       4
 #  define glue_ptr(a)          vinfo((a)+2)
@@ -993,7 +1005,7 @@ extern halfword new_param_glue(int n);
 extern halfword new_glue(halfword q);
 extern halfword new_skip_param(int n);
 extern halfword new_kern(scaled w);
-extern halfword new_penalty(int m);
+extern halfword new_penalty(int m, int s);
 
 extern int lua_properties_enabled ;
 extern int lua_properties_level ;
diff --git a/source/texk/web2c/luatexdir/tex/texnodes.w b/source/texk/web2c/luatexdir/tex/texnodes.w
index 40efbd2ab0b43c7c80ffd22a641637701f06bb5b..7967006ec35986ec79b4a9754d2318f6c5675385 100644
--- a/source/texk/web2c/luatexdir/tex/texnodes.w
+++ b/source/texk/web2c/luatexdir/tex/texnodes.w
@@ -262,7 +262,8 @@ const char *node_subtypes_boundary[] = {
     "cancel", "user", "protrusion", "word", NULL
 };
 const char *node_subtypes_penalty[] = {
-    "userpenalty", NULL
+    "userpenalty", "linebreakpenalty", "linepenalty", "wordpenalty", "finalpenalty",
+    "noadpenalty", "beforedisplaypenalty", "afterdisplaypenalty", "equationnumberpenalty", NULL
 };
 const char *node_subtypes_kern[] = {
     "fontkern", "userkern", "accentkern", "italiccorrection", NULL
@@ -3692,10 +3693,11 @@ break will be forced.
 be able to guess what comes next.
 
 @c
-halfword new_penalty(int m)
+halfword new_penalty(int m, int s)
 {
     halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */
     penalty(p) = m;
+    subtype(p) = s;
     return p;
 }