[latex3-commits] [git/LaTeX3-latex3-latex2e] marks: testing for and handling infinite shrinkage (aedc52fa)

Frank Mittelbach frank.mittelbach at latex-project.org
Mon Apr 4 10:41:00 CEST 2022


Repository : https://github.com/latex3/latex2e
On branch  : marks
Link       : https://github.com/latex3/latex2e/commit/aedc52fa0a2df1e85bf594251dbf1cb9b9af14b8

>---------------------------------------------------------------

commit aedc52fa0a2df1e85bf594251dbf1cb9b9af14b8
Author: Frank Mittelbach <frank.mittelbach at latex-project.org>
Date:   Mon Apr 4 10:41:00 2022 +0200

    testing for and handling infinite shrinkage


>---------------------------------------------------------------

aedc52fa0a2df1e85bf594251dbf1cb9b9af14b8
 base/ltmarks.dtx | 135 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 105 insertions(+), 30 deletions(-)

diff --git a/base/ltmarks.dtx b/base/ltmarks.dtx
index 1f2229e7..ada4f4e7 100644
--- a/base/ltmarks.dtx
+++ b/base/ltmarks.dtx
@@ -15,7 +15,7 @@
 %
 %    \begin{macrocode}
 \def\ltmarksversion{v1.0a}
-\def\ltmarksdate{2022/04/02}
+\def\ltmarksdate{2022/04/04}
 %    \end{macrocode}
 %<*driver>
 \documentclass{l3doc}
@@ -766,7 +766,35 @@
   \group_begin:
     \dim_set:Nn \tex_splitmaxdepth:D \c_max_dim
     \int_set:Nn \tex_vbadness:D      \c_max_int
-    \vbox_set:Nn \l_@@_box
+    \dim_set:Nn \tex_vfuzz:D         \c_max_dim
+%    \end{macrocode}
+%    There is a further complication: if the region contains infinite
+%    shrinking glue then a \cs{vsplit} operation will balk with a
+%    low-level error. Now pages or columns, which is our main concern here, can't
+%    have such infinite shrinkage if they are cut straight from the
+%    galley, however the use of \cs{enlargethispage} actually does add
+%    some at the very bottom (and also wraps the whole page into a box
+%    by itself, so if we leave it this way then a) we get this error
+%    and b) we don't see any marks because they are hidden one level
+%    down).
+%    
+%    We therefore do an unskip to get rid of that glue if present and
+%    also check if we have then a \cs{vbox} as the last item and if so
+%    unpack that too. All this is temporary, just for getting the
+%    marks out, so it doesn't affect the final page production. 
+%    
+%    In fact we go one step further and set the box to a large
+%    negative height possible and afterwards take a look at the
+%    reported badness: if it is zero we know that there has still been
+%    infinite shrinkage in the box so that we can't do a
+%    \cs{vsplit}. If that is the case we generate an error message and
+%    bypass extracting the marks. We use only half of \cs{c_max_dim}
+%    because otherwise \TeX{} will report an overfull vbox despite our
+%    setting of \cs{tex_vfuzz:D}. This test will not find existing
+%    infinite shrinkage in all case, e.g., if there are several glues
+%    that cancel each other, but it is the best we can do.
+%    \begin{macrocode}
+    \vbox_set_to_ht:Nnn \l_@@_box { -.5\c_max_dim }
        {
          #2
          \tex_unskip:D
@@ -774,15 +802,25 @@
          \box_if_vertical:NT \l_@@_box
              { \vbox_unpack:N \l_@@_box }
        }
-    \vbox_set_split_to_ht:NNn \l_@@_box \l_@@_box \c_max_dim
+    \int_compare:nNnTF \tex_badness:D > 0
+%    \end{macrocode}
+%    If the box had no infinite shrinkage (or rather if our test
+%    didn't show any) we vsplit it. Note that it
+%    doesn't matter that we set it to this strange size first. If the
+%    was infinite shrinkage after all, we end up with a low-level
+%    \TeX{} error, but if there is it is a coding error and needs
+%    correcting.
+%    \begin{macrocode}
+       {
+         \vbox_set_split_to_ht:NNn \l_@@_box \l_@@_box \c_max_dim
 %    \end{macrocode}
 %    After this action we can get first and last marks of the various
 %    classes through \cs{tex_splitfirstmarks:D} and
 %    \cs{tex_splitbotmarks:D}. So now we loop over all classes stored in
 %    \cs{g_@@_classes_seq}.
 %    \begin{macrocode}
-    \seq_map_inline:Nn \g_@@_classes_seq
-      {
+         \seq_map_inline:Nn \g_@@_classes_seq
+           {
 %    \end{macrocode}
 %    First action: get the last mark from the previous region, i.e.,
 %    \verb=previous-#1= but because it is also still inside \verb=#1=
@@ -791,17 +829,17 @@
 %    need this value in various assignments we store it away which
 %    avoids unnecessary further csname generations.
 %    \begin{macrocode}
-        \tl_gset_eq:Nc \g_@@_new_top_tl { g_@@_ #1 _last_  ##1 _tl }
+             \tl_gset_eq:Nc \g_@@_new_top_tl { g_@@_ #1 _last_  ##1 _tl }
 %    \end{macrocode}
 %    This will first of all become the new top mark for the current class.
 %    \begin{macrocode}
-        \tl_gset_eq:cN { g_@@_ #1 _top_ ##1 _tl } \g_@@_new_top_tl
+             \tl_gset_eq:cN { g_@@_ #1 _top_ ##1 _tl } \g_@@_new_top_tl
 %    \end{macrocode}
 %    Next action is to get ourselves the new last mark from the
 %    material supplied.
 %    \begin{macrocode}
-        \tl_gset:No \g_@@_tmp_tl
-           { \tex_splitbotmarks:D \use:c { c_@@_class_ ##1 _mark } }
+             \tl_gset:No \g_@@_tmp_tl
+                { \tex_splitbotmarks:D \use:c { c_@@_class_ ##1 _mark } }
 %    \end{macrocode}
 %    If this mark doesn't exist then obviously first mark does
 %    neither, so both become the last mark from previous region. We
@@ -813,17 +851,17 @@
 %    appears to be empty but fails the next test (see below how this
 %    is done).
 %    \begin{macrocode}
-        \tl_if_empty:NTF \g_@@_tmp_tl 
-          {
-            \tl_gset_eq:cN { g_@@_ #1 _last_  ##1 _tl } \g_@@_new_top_tl
-            \tl_gset_eq:cN { g_@@_ #1 _first_ ##1 _tl } \g_@@_new_top_tl
-          }
+                \tl_if_empty:NTF \g_@@_tmp_tl 
+                  {
+                    \tl_gset_eq:cN { g_@@_ #1 _last_  ##1 _tl } \g_@@_new_top_tl
+                    \tl_gset_eq:cN { g_@@_ #1 _first_ ##1 _tl } \g_@@_new_top_tl
+                  }
 %    \end{macrocode}
 %    If it wasn't empty, i.e., if it had a real value then we use this
 %    value for our new last mark instead. 
 %    \begin{macrocode}
-          {
-            \tl_gset_eq:cN { g_@@_ #1 _last_ ##1 _tl } \g_@@_tmp_tl
+                 {
+                   \tl_gset_eq:cN { g_@@_ #1 _last_ ##1 _tl } \g_@@_tmp_tl
 %    \end{macrocode}
 %    Because we had a last mark we also have a first mark (which
 %    might be the same, but might be not), so we pick that up and
@@ -831,10 +869,26 @@
 %    checked for the last mark because that makes the processing
 %    faster in case there is none.
 %    \begin{macrocode}
-            \tl_gset:co { g_@@_ #1 _first_ ##1 _tl }
-               { \tex_splitfirstmarks:D \use:c { c_@@_class_ ##1 _mark } }
-          }
-      }
+                   \tl_gset:co { g_@@_ #1 _first_ ##1 _tl }
+                      { \tex_splitfirstmarks:D \use:c { c_@@_class_ ##1 _mark } }
+                 }
+           }
+       }
+%    \end{macrocode}
+%    If the badness was zero (we actually tested for ${}>0$ but it
+%    can't get negative) then we had infinite shrinkage, so we report
+%    that and set all marks to the value the last mark had before.
+%    \begin{macrocode}
+       {
+         \msg_error:nnn { mark } { infinite-shrinkage } {#1}
+         \seq_map_inline:Nn \g_@@_classes_seq
+           {
+             \tl_gset_eq:cc { g_@@_ #1 _top_ ##1 _tl }
+                            { g_@@_ #1 _last_  ##1 _tl }
+             \tl_gset_eq:cc { g_@@_ #1 _first_ ##1 _tl }
+                            { g_@@_ #1 _last_  ##1 _tl }
+           }
+       }
 %    \end{macrocode}
 %    Once all mark classes have been processed the data structures are
 %    up dated and we can close the group which undoes our local
@@ -1049,6 +1103,18 @@
   }
 %    \end{macrocode}
 %
+%    \begin{macrocode}
+\msg_new:nnnn { mark } { infinite-shrinkage }
+  { Infinite~shrinkage~found~in~'#1'. }
+  {
+    \c__msg_coding_error_text_tl
+    The~mark~region~'#1'~contains~some~infinite~negative~glue~
+    allowing~it~to~shrink~to~an~arbitrary~size.~
+    This~makes~it~impossible~to~split~the~region~apart~to~
+    get~at~its~marks.~They~are~lost.
+  }
+%    \end{macrocode}
+%
 %
 %
 % \subsection{Tracing the mark structures}
@@ -1204,20 +1270,21 @@
 %    \end{macrocode}
 %    First we update the \texttt{page} region (which also updates the
 %    \texttt{previous-page}.
-  %    \begin{macrocode}
-  \@@_update_structure:nn {page}
-     {
-%    \end{macrocode}
+%
 %    The \cs{@outputbox} is normally in \cs{vbox} in \LaTeX{} but we
 %    can't take that for granted (an \texttt{amsmath} test document
 %    changed it to an \cs{hbox} just to trip me up) so we are a little
 %    careful with unpack now.
 %    \begin{macrocode}
-       \box_if_vertical:NTF \@outputbox
-           { \vbox_unpack:N }
-           { \hbox_unpack:N }
-       \@outputbox
-     }
+  \box_if_vertical:NTF \@outputbox
+      {
+        \@@_update_structure:nn {page}
+           { \vbox_unpack:N  \@outputbox }
+      }
+      {
+        \@@_update_structure:nn {page}
+           { \hbox_unpack:N  \@outputbox }
+      }
 %    \end{macrocode}
 %    The we provide the necessary updates for the aliases.
 %    \begin{macrocode}
@@ -1250,7 +1317,15 @@
 %    First we update the \texttt{column} and \texttt{previous-column}
 %    regions using the material assembled in \cs{@outputbox}.
 %    \begin{macrocode}
-  \@@_update_structure:nn {column}{ \unvcopy\@outputbox }
+  \box_if_vertical:NTF \@outputbox
+      {
+        \@@_update_structure:nn {column}
+           { \vbox_unpack:N  \@outputbox }
+      }
+      {
+        \@@_update_structure:nn {column}
+           { \hbox_unpack:N  \@outputbox }
+      }
 %    \end{macrocode}
 %    How we have to update the alias regions depends on whether or not
 %    \cs{@opcol} was called to process the first column or to produce





More information about the latex3-commits mailing list.