如果有脚注,获取其位置

如果有脚注,获取其位置

简洁版本:当有脚注时,我怎样才能获得红色框的位置以便在脚注之前停止我的行(tcolorbox 设法在良好的位置停止行)?

在此处输入图片描述

更长的版本 为了解决这个问题并有缩进的证明,例如:

在此处输入图片描述

我使用 来tikzpagenodes查找当前文本页面边框的位置。不幸的是,当存在脚注时,结果很难看,因为线条穿过了脚注的文本。

平均能量损失

\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,
}

\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, which goes too deep.
    \tikz[remember picture,overlay] \draw (pic cs:hello) -- ({pic cs:hello}|-{current page text area.south east});
    \lipsum[1-3].
  \end{adjustwidth}
\end{proof}


\lipsum[1]

\end{document}

编辑

在 gusbrs 的第一个有希望的指针之后,事实证明它lineno并没有给出方程的行号......因此在我的情况下它并不理想,因为我可能有许多方程式:

在此处输入图片描述

此外,如果存在某些图像,它不会考虑深度:

在此处输入图片描述

我想挂接到绘制脚注的命令以添加一些 tikzmark(上图中的绿色线),有人知道在脚注环境开始时执行的命令的名称吗?

答案1

感谢 gusbrs 的指点\footnoterule,我设法通过将 tikzmark 挂接到此函数中来使其工作。现在我需要稍微提高一点终点,否则它实际上会触及\footnoterule

在此处输入图片描述

如果我将其提高\baselineskip,它并不总是像如下所示那样提高tcolorbox(tcolorbox 似乎根据排版的内容进行调整):

在此处输入图片描述

因此,为了保持一致的外观,我需要摆脱它,tcolorbox但无论如何,这是计划好的。

现在,为了让最初的计划奏效,我需要为每页创建一个这样的标记,检查是否有一些……但所有这些都应该是可行的。通往子证明的道路并不那么容易^^


\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}

编辑

我设法让它完全发挥作用。首先创建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

同一文件夹中还有一个文件,其中包含:

\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}

我应该尝试为此创建一个包。

相关内容