简洁版本:当有脚注时,我怎样才能获得红色框的位置以便在脚注之前停止我的行(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}
我应该尝试为此创建一个包。