Thanks to gusbrs pointing \footnoterule
, I managed to get it working by hooking tikzmark into this function. For now I need to raise a bit the end point otherwise it actually touches the \footnoterule
:
If I raise it by \baselineskip
, it does not always raise it exactly like tcolorbox
(tcolorbox seems to adapt depending on the content that is typeset):
So in order to keep a consistent look I'll need to get rid of tcolorbox
But anyway it was planned.
Now, to make the initial plan work, I'll need to create one such marker per page, check if there are some… but all of this should be doable. The path to subproofs is not that easy ^^
\documentclass[]{memoir}
\usepackage{tikz}
\RequirePackage{amsmath}
\RequirePackage{amsthm}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc,tikzmark}
%%% Tcolorbox does a pretty good job to find the position where lines should be stopped...
%%% but they can't be nested.
\RequirePackage[many]{tcolorbox}
\tcolorboxenvironment{proof}{
blanker,
before skip=\topsep,
after skip=\topsep,
borderline west={0.4pt}{0.4pt}{black},
breakable,
left=2.5mm,
grow to left by=2.5mm,
}
%% On new Latex (>2020):
% \AddToHook{cmd/footnoterule/before}{\tikzmark{mymarknoterule}}
%% On old Latex
\let\oldfootnoterule\footnoterule
\def\footnoterule{\tikzmark{mymarknoterule}\oldfootnoterule}
\usepackage{lipsum}
\begin{document}
Here is my document. Blabla \footnote{Here is my first foot note.} blabla \footnote{Here is my second foot note.}.
\begin{proof}
\lipsum[1-2]
\begin{adjustwidth}{5mm}{0mm}
\tikzmark{hello}
%% Draw the line, and raise a bit the end or it will literally touch the foot note rule.
\tikz[remember picture,overlay] \draw (pic cs:hello) -- ([yshift=\baselineskip-2pt]{{pic cs:hello}|-{pic cs:mymarknoterule}});
\lipsum[1-3].
\end{adjustwidth}
\end{proof}
\lipsum[1]
\end{document}
EDIT
I managed to make it work in full generality. First create subproof.sty
:
% Some questions I asked: https://tex.stackexchange.com/questions/623412/get-position-of-footnote-if-there-are-some
\RequirePackage{tikzpagenodes}
\RequirePackage{tikz}
\usetikzlibrary{calc,tikzmark} %% We use tikzmark... but we have issues sometimes. Maybe pdfsavepos is more resilient? Load with \usepackage{zref-savepos}, put a point like \zsavepos{blibli}, use like ([xshift=\zposx{blibli}sp,yshift=\zposy{blibli}sp]current page.south west)
\RequirePackage{everypage}
\RequirePackage{ifthen}
\RequirePackage{changepage}
\RequirePackage{environ}
\RequirePackage{amsmath}
% \RequirePackage{amsthm}
\RequirePackage[many]{tcolorbox} % User must load tcolorbox after this I think
\RequirePackage{letltxmacro}
\RequirePackage{caption} % to reduce the size of the caption to avoid overlap
\RequirePackage{zref-savepos}
\usepackage[abspage,user,lastpage]{zref} % Useful to get the current page number.
\def\subproofsDefaultShift{5mm}
\def\proofsDefaultShift{2.5mm}
% Shift towards the center of the line
\def\subproofsDefaultFirstPointYShift{.3em} %% Better results when using baselineskip
%\def\subproofsDefaultFirstPointYShift{0em} %% Better results when using `\hrule height0pt`
\def\subproofsDefaultSecondPointYShift{0em}
\definecolor{subproof-dark-gray}{gray}{0.70}
\def\colorFrame{subproof-dark-gray}
%%% To deal with footnotes
%% On new Latex (>2020):
% \AddToHook{cmd/footnoterule/before}{\tikzmark{mymarknoterule}}
%% On old Latex. \c@abspage is provided by zref to get the current page (not really official as far as I can say).
\let\oldfootnoterule\footnoterule
\def\footnoterule{\edef\my@tmp{\noexpand\tikzmark{subproofPageNumberFootnote\the\c@abspage}}\my@tmp\oldfootnoterule}
\tikzset{
/subproofs/defaultStyle/.style={
line/.style={
\colorFrame,%black!30!white,
line width=.4pt,
transform canvas={xshift=-\subproofsDefaultShift/2},
},
topPoint/.style={yshift=-\subproofsDefaultFirstPointYShift},
bottomPoint/.style={yshift=\subproofsDefaultSecondPointYShift},
topPointCut/.style={},
bottomPointCut/.style={},
hittingFootnote/.style={yshift=\baselineskip-2pt},
},
/subproofs/proofStyle/.style={
line/.style={
\colorFrame,%black!30!white,
line width=.4pt,
transform canvas={xshift=-\subproofsDefaultShift/2},
},
% Use .append style to add to the default style.
topPoint/.style={yshift=2ex},
bottomPoint/.style={yshift=-.3ex},
topPointCut/.style={},
bottomPointCut/.style={},
},
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Deal with drawings and marking.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Warning: tikz does not like ExplSyntax since it removes spaces
\def\addTikzmarkAndPagesInfo#1{%
\tikzmark{beginTikzmark#1}%
\tikz[remember picture, overlay] \coordinate (pageInfoNWOf#1) at (current page text area.north west);%
\tikz[remember picture, overlay] \coordinate (pageInfoSEOf#1) at (current page text area.south east);%
}
% Make sure to enclose in a group
\def\subproofSwapToProofStyle{%
\tikzset{/subproofs/defaultStyle/.append style={
/subproofs/proofStyle
}}%
}
\def\drawLinesOfTikzmarkSamePage#1{%
\tikz[remember picture, overlay]%
\draw[/subproofs/defaultStyle, line]
([/subproofs/defaultStyle, topPoint]pic cs:beginTikzmark#1) to
([/subproofs/defaultStyle, bottomPoint]
{{pic cs:endTikzmark#1}-|{pic cs:beginTikzmark#1}});%
}
% \def\drawLinesOfTikzmarkStart#1{%
% \tikz[remember picture, overlay]%
% \draw[/subproofs/defaultStyle, line] ([/subproofs/defaultStyle, topPoint]pic cs:beginTikzmark#1) to ([/subproofs/defaultStyle, bottomPointCut]{{pic cs:beginTikzmark#1}|-{current page text area.south west}});%
% }
\def\drawLinesOfTikzmarkStart#1{%
% I'm not sure why but the page number is always ahead of one (maybe the drawing starts right after zref increases the counter? TODO: check that the order of loading packages does not change this fact)
% So lets decrease it temporaly by one…
\addtocounter{abspage}{-1}% \c@abspage is a counter of name abspage…
% Check if there is a footnote on the current page:
\edef\subproof@current@footnote{subproofPageNumberFootnote\the\c@abspage}%
\addtocounter{abspage}{1}%… and now we can reset it to its initial value
\expandafter\iftikzmarkoncurrentpage{\subproof@current@footnote}% There is a footnote on the current page
\tikz[remember picture, overlay]\draw[/subproofs/defaultStyle, line] ([/subproofs/defaultStyle, topPoint]pic cs:beginTikzmark#1) to ([/subproofs/defaultStyle, hittingFootnote]{{pic cs:beginTikzmark#1}|-{pic cs:\subproof@current@footnote}});%
\else% There is no footnote on the current page
\tikz[remember picture, overlay]\draw[/subproofs/defaultStyle, line] ([/subproofs/defaultStyle, topPoint]pic cs:beginTikzmark#1) to ([/subproofs/defaultStyle, bottomPointCut]{{pic cs:beginTikzmark#1}|-{current page text area.south west}});%
\fi%
}
\def\drawLinesOfTikzmarkMiddle#1{%
% I'm not sure why but the page number is always ahead of one (maybe the drawing starts right after zref increases the counter? TODO: check that the order of loading packages does not change this fact)
% So lets decrease it temporaly by one…
\addtocounter{abspage}{-1}% \c@abspage is a counter of name abspage…
% Check if there is a footnote on the current page:
\edef\subproof@current@footnote{subproofPageNumberFootnote\the\c@abspage}%
\addtocounter{abspage}{1}%… and now we can reset it to its initial value
\expandafter\iftikzmarkoncurrentpage{\subproof@current@footnote}% There is a footnote on the current page
\tikz[remember picture,overlay] \draw[/subproofs/defaultStyle, line] %
let%
\p1=(pic cs:beginTikzmark#1),%
\p2=(pageInfoNWOf#1),%
\p3=(current page text area.north west),%
\p4=(current page text area.south east) in%
([/subproofs/defaultStyle, topPointCut]{\x1-\x2+\x3},\y3) to ([/subproofs/defaultStyle, hittingFootnote]{{pic cs:beginTikzmark#1}|-{pic cs:\subproof@current@footnote}});%
\else% There is no footnote on the current page
\tikz[remember picture,overlay] \draw[/subproofs/defaultStyle, line] %
let%
\p1=(pic cs:beginTikzmark#1),%
\p2=(pageInfoNWOf#1),%
\p3=(current page text area.north west),%
\p4=(current page text area.south east) in%
([/subproofs/defaultStyle, topPointCut]{\x1-\x2+\x3},\y3) to ([/subproofs/defaultStyle, bottomPointCut]{\x1-\x2+\x3},\y4);%
\fi%
}
\def\drawLinesOfTikzmarkEnd#1{%
%\iftikzmark{pageInfoNWOf#1}{% Sometimes I get errors about pageInfoNWOf not found, that solve after clearning aux files... not sure why. Seems like this issue is gone after reordering some stuff.
\tikz[remember picture,overlay] \draw[/subproofs/defaultStyle, line] %
let%
\p1=(pic cs:beginTikzmark#1),%
\p2=(pageInfoNWOf#1),%
\p3=(current page text area.north west),%
\p4=(current page text area.south east),%
\p5=(pic cs:endTikzmark#1) in %
([/subproofs/defaultStyle, topPointCut]{\x1-\x2+\x3},\y3) to ([/subproofs/defaultStyle, bottomPoint]{\x1-\x2+\x3},\y5);%
%}{}
}
%\makeatletter
% \checkmarkpage{label}{if before}{if same page}{if after}
% Apply different codes if we are on a page before, the current page,
\newcommand{\checkmarkpage}[4]% #1 = tikzmark label, #2 = less, #3 = equal, #4 = greater
{\@ifundefined{save@pt@#1}{#2}{%
\edef\markid{\csname save@pt@#1\endcsname}%
\edef\markpage{\csname save@pg@\markid\endcsname}%
\ifnum\thepage<\markpage\relax #2%
\else
\ifnum\thepage=\markpage\relax #3%
\else #4%
\fi
\fi}%
}
%\makeatother
%%% Starts expl3 syntax https://mirrors.concertpass.com/tex-archive/macros/latex/contrib/l3kernel/expl3.pdf
%%% For the list of modules and everything http://linorg.usp.br/CTAN/macros/latex/contrib/l3kernel/interface3.pdf
%%% (this also provides a quickstart at the beginning)
%%% For functions https://tex.stackexchange.com/questions/492794/how-to-define-two-expl3-functions-with-the-same-base-name-and-different-signatur
%%% This code is certainly not an example of LaTeX programming as it's the first time I use expl3...
\ExplSyntaxOn
% l=local, name, type. Note that this is only convention, it could be named "\foo" instead.
% clist are list separated by commas.
% To remove stuff https://tex.stackexchange.com/questions/5754/delete-an-element-from-a-comma-delimited-list
\seq_new:N\l_ListOfLinesToDraw_seq{}
\newcounter{nextMarkId}
\stepcounter{nextMarkId} %% Make sure it is not zero, I'm not sure roman works for them.
% Inspired by https://tex.stackexchange.com/questions/528774/excess-vertical-space-in-vdots/528775#528775
% See also https://tex.stackexchange.com/questions/622881/align-element-tikzmark-with-top-of-the-current-line-instead-of-baseline/622936#622936
% TODO: Read also https://fr.overleaf.com/learn/latex/Articles/Boxes_and_Glue%3A_A_Brief%2C_but_Visual%2C_Introduction_Using_LuaTeX
%%%% Uncomment to have the "baselinemode" (and comment next definition)
\long\def\addZeroWidthLine#1{%
{%
\baselineskip=0pt%
\lineskip=0pt%
\lineskiplimit=0pt%
\parskip=0pt%
\noindent#1\par\nointerlineskip\nobreak%
}%
}
% nointerlineskip seems to be a problem for linedproof
\long\def\nicerAddZeroWidthLine#1{%
{%
\baselineskip=0pt%
\lineskip=0pt%
\lineskiplimit=0pt%
\noindent#1\par\nobreak%
}%
}
%%%% Uncomment to have the "hrule" version:
% \long\def\addZeroWidthLine#1{%
% \vskip\lineskip\hrule height0pt\noindent#1\hrule height0pt\vskip\lineskip%
% }
\NewEnviron{subproof}{%
\par\edef\thisMarkId{\thenextMarkId}% Temporary variable to use at the end.
\seq_gput_right:NV \l_ListOfLinesToDraw_seq \thisMarkId % Add it to the list
\begin{adjustwidth}{\subproofsDefaultShift}{0cm}\nobreak%
\nicerAddZeroWidthLine{\addTikzmarkAndPagesInfo{\thenextMarkId}}\nopagebreak% <- check if nopagebreak works
%\dealWithOneLine:V{\thisMarkId}% Draw the line for the current one.
\stepcounter{nextMarkId}%
% Ensures a group is inserted around the BODY
{%
\advance\textwidth\dimexpr-\subproofsDefaultShift/2\relax% Hum... Is it safe to do that? linewidth is properly configured.
% columnwidth moves image towards the left... not ideal.
%\advance\columnwidth\dimexpr-1.1\subproofsDefaultShift/2\relax% for "float" library. Unfortunately it is flushed left.
% Reduce the width of the caption of the float images to avoid overlap.
\captionsetup{width=.9\linewidth} % The width seems actually smaller than linewidth, but it does the trick.
\noindent \BODY \par\nobreak}% nobreak is used to ensure the tikzmark is not pushed on a new page.
\nicerAddZeroWidthLine{\tikzmark{endTikzmark\thisMarkId}}\nopagebreak%
\end{adjustwidth}%
}%
%%% Create a new environment whose name is the content of the variable \nameProofTcolorboxEnvironment.
%%% In case it does not exists, it defaults to prooftcolorbox
% Save the original proof environment
\LetLtxMacro\OriginalProofEnv\proof
\LetLtxMacro\endOriginalProofEnv\endproof
\ifdefined\nameMainProofEnvironment\else%
\def\nameMainProofEnvironment{proof}%
\fi
% DeclareDocumentEnvironment will overwrite the environment if it exists
% The proof source is given in https://ctan.org/tex-archive/macros/latex/required/amscls.
% search \newenvironment{proof} in amsclass.dtx.
% Warning: it is important that the starting point of the proof text starts with \proofname (defaults to Proof).
\expandafter\DeclareDocumentEnvironment{\nameMainProofEnvironment}{}{%
\let\subproof@oldProofname\proofname% Keep in memory the old name of the proof.
%% Warning: the name must be different than the one in subproof, otherwise qedhere inside a subproof will not
%% work as it will take the value of the subproof.
\edef\thisMarkIdProof{\thenextMarkId}% Temporary variable to use at the end of the environment.
%%%%% Deal with the style, by creating a new macro whose name is subproof@style\thisMarkIdProof containing the style.
% Temporary variable to store the name of the macro containing the style.
\edef\subproof@tmp@name@macro{subproof@style\thisMarkIdProof}%
% xdef is like global + evaluate (gdev + edef)
\expandafter\xdef\csname \subproof@tmp@name@macro\endcsname{/subproofs/proofStyle}%
%%% For security, write it into the aux file in case we try to load it before (should not append in practice)
%\write\@auxout{\string\gdef\string\subproof@style\thisMarkIdProof{/subproofs/proofStyle}}%
\renewcommand\proofname{% Replace "Proof" or "Proof of XXX" with "\tikzmark{...}Proof":
\seq_gput_right:NV \l_ListOfLinesToDraw_seq \thisMarkIdProof% Add it to the list
\addTikzmarkAndPagesInfo{\thisMarkIdProof}% Add the tikz mark for the starting point
{% Make sure we create a group for the tikzset
\subproofSwapToProofStyle%
%\dealWithOneLine:V{\thisMarkIdProof}% Draw the line for the current one.
}%
\subproof@oldProofname% Calls the original proof name
}%
%%% Redefines now the qed symbol.
\let\subproof@oldQed\qed%
\renewcommand\qed{% append to the symbol that ends the proof with our tikzwork
\subproof@oldQed% Insert back the square at the end
\tikzmark{endTikzmark\thisMarkIdProof}% Insert the tikz
}%
\OriginalProofEnv% Write back the "Proof of XXX"...
\stepcounter{nextMarkId}% Increases the counter for the next environment.
}{%
\endOriginalProofEnv%
}
%% Create a new function (see http://linorg.usp.br/CTAN/macros/latex/contrib/l3kernel/interface3.pdf page 14)
\cs_new:Nn \dealWithOneLine:n{
\bgroup%
%%% Styling: subproofStyle#1 contains the name of the style to add
\edef\subproof@namestyle{subproof@style#1}% Name of the macro containing the style, like subproof@style0
\ifcsname \subproof@namestyle\endcsname% Check if this macro exists.
\tikzset{% Add the corresponding style:
%% \space is necessary as explsyntax removes spaces (yes, I still use %... I'm too worried now.)
/subproofs/defaultStyle/.append\space style={%
\csname\subproof@namestyle\endcsname%
}%
}%
\fi%
\checkmarkpage{beginTikzmark#1}{%
%%%%%%%%%% We have not yet seen the beginTikzMark... Don't care.
}{ %%%%%%%%%% We are on the page of the start.
\checkmarkpage{endTikzmark#1}%
{%%%%%%%%%% The end is later
\drawLinesOfTikzmarkStart{#1}%
%\printList:N{} Size is \seq_count:N \l_ListOfLinesToDraw_seq%
}%
{%%%%%%%%%% The end is on the same page
\drawLinesOfTikzmarkSamePage{#1}%
%% We can remove the item from the list so that we don't process it anymore.
\seq_gremove_all:Nn \l_ListOfLinesToDraw_seq {#1}%
%\printList:N{} Size is \seq_count:N \l_ListOfLinesToDraw_seq%
}{% The end was on a previous page... impossible
}%
}{ %%%%%%%%%% The start point is on a previous page
\checkmarkpage{endTikzmark#1}%
{%%%%%%%%%% The end is later
\drawLinesOfTikzmarkMiddle{#1}%
%\printList:N{} Size is \seq_count:N \l_ListOfLinesToDraw_seq%
}%
{%%%%%%%%%% The end is on the same page
\drawLinesOfTikzmarkEnd{#1}%
%% We can remove the item from the list so that we don't process it anymore.
\seq_gremove_all:Nn \l_ListOfLinesToDraw_seq {#1}%
%\printList:N{} Size is \seq_count:N \l_ListOfLinesToDraw_seq%
}{%%%%%%%%%% The end has already been seen... I don't care
}%
}%
\egroup%
}
% defines \dealWithOneLine:V that expands its argument
\cs_generate_variant:Nn \dealWithOneLine:n { V }
%% Create a new function (see http://linorg.usp.br/CTAN/macros/latex/contrib/l3kernel/interface3.pdf page 14)
\cs_new:Nn \dealWithAllLines:N {
\seq_map_inline:Nn \l_ListOfLinesToDraw_seq {
\dealWithOneLine:n{##1}
}
}
%% Create a new function (see http://linorg.usp.br/CTAN/macros/latex/contrib/l3kernel/interface3.pdf page 14)
\cs_new:Nn \printList:N {
[
\seq_map_inline:Nn \l_ListOfLinesToDraw_seq {
##1,
}
]
}
\AddEverypageHook{\dealWithAllLines:N{}}
\ExplSyntaxOff
and a file in the same folder containing:
\documentclass[options]{article}
\usepackage{subproof}
\usepackage{lipsum}
\begin{document}
\lipsum[1]
\begin{subproof}
Blabla\footnote{See, footnotes work nicely now!}
\begin{subproof}
\lipsum[1-2]
\end{subproof}
Bloblo
\begin{subproof}
\lipsum[3-4]
\end{subproof}
Bloblo\footnote{See, footnotes work nicely now!}\footnote{See, footnotes work nicely now!}\footnote{See, footnotes work nicely now!}
\begin{subproof}
\lipsum[3-8]
\end{subproof}
\end{subproof}
\end{document}
I should try to create a package for that.
lineno
for my actual use case, but unfortunately I don't recall what it was. Well, it is a matter of trying it out. :) And, in your case, it might actually be somewhat easier, since "something" exists (the footnote) which you could leverage. Perhaps you could place atikzmark
or azref
savepos
label on your footnotes. Idk.lineno
is that it does not give numbers to equation. So if my page ends with an equation it will not be included into the line... Concerning the footnotes, I'd love to find a way to hook into them, but not sure on which command I should hook into. Notably, I see that a line is drawn to separate footnotes from the body... what is the command that typesets this line?\footnoterule
, if I'm not mistaken. It is actually a good place I think: it is an argumentless macro, and occurs only once per page, contrary to the footnote itself, which can be multiple. You could use the new hook management system and place a\tikzmark
there. That would give you a reliable y coordinate.