LaTeX forum ⇒ Page LayoutA footnote/footnotemark solution to make it compatible with figure/floatrow/hyperref and backreference

Information and discussion about page layout specific issues (e.g. header and footer lines, page formats, page numbers).
Nilsou
Posts: 2
Joined: Tue Mar 24, 2020 4:12 pm

A footnote/footnotemark solution to make it compatible with figure/floatrow/hyperref and backreference

Postby Nilsou » Tue Mar 24, 2020 4:59 pm

(edit1 to delete a warning)
Hi !
I have searched a long time for a solution that works in a lot of environment (figure, floatrow, text etc...) which allow footnote to work decently and with hyperlinks activated in reference way (from the footnote to the footnote text) and in backreference way (from the footnote text to the footnote).

By combining several packages, I achieved several partial solutions but never succeed in making a full homogeneous solution.

After some digging, a lot of questions everywhere and some coding I achieve to obtain a solution that works in all cases I have tested. (see limitations at the bottom of the post)

So well, I post it here to those who want to use it because it is very useful in my case, but beware : it is a bit technical.
The idea was, in first time, to make a reliable solution for the command footnote{} to make it work with hyperref and backreference in all environment, even exotic one. It is the easy part.

The second point was to allow the use of footnotemark/footnotetext without [] in figure and floatrow with the support of hyperref and back reference because classical footnote tends to be useful if you want footnote at the bottom of figure but not very useful to make footnote from figure or other environment insert versatily in the others defined in plaintext. Not too difficult either but it is not very useful if you want to have multi footnone in a figure or in a floatrow .

The final point, which is the more tricky was to change the behaviour of footnotemark[] / footnotetext[] to a label/ref like behaviour. Indeed : footnotemark[]/footnotetext[] vanilla can be used inside several environnement with manual numbering. And some tricks from the hyperref manual (http://ctan.tetaneutral.net/macros/latex/contrib/hyperref/doc/manual.pdf) allow using footnotemark and footnotetext with reference on and a simple label/hyperref tricks can help with backreference. But all of this give a looooot of ugly code in the text file, which is not very practical.

So here the idea is to transform this function to include all of these tricks and to change the behaviour to a label/ref one.

A footref command slightly tuned allow to reference indiferently to old footnote/footnotetext/footnotetext[] defined elsewhere in the code. And other tricks allow to call footnotetext before or after the footnotemark indiferently.
A final big tricks allows the code to automatically defined if it is the first footnotemark called (and the tricks is not so simple in environment like floatrow) and switch automatically to a footref in this case to make references to the old footnotemark without disturbing the footnotetext thing.


The result :
Image

The result pdf to test hyperref and backreference :
https://mega.nz/#!zGAgDK4a!SDWr_4c-Dsq9nQZFfr0jRfN6m-XQcdqyfpx069nGLMQ

The (long) code :
%%%%%%%%%%% LOT OF PACKAGES AND DEFINITIONS %%%%%%%%%%
%\pdfobjcompresslevel 0
%

%%%% work with koma-script, should also work on standard classes %%%%
\documentclass[a4paper,12pt,oneside,final, DIV=12, listof=totoc, bibliography=totoc, toc=bibliography, open=right, chapterprefix=true]{scrbook} % If you want to use the option footnote=multiple you have to copy some code of Koma script about footnotemark and patch this file to make it compatible

\usepackage[greek,english,french]{babel} % It work with or without babel french (see the patch at the bottom)

% I have not tried with other encoding or other font
\usepackage{lmodern} 
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{graphicx} % only for testing
\usepackage[final]{hyperref} % for the links

%%% some package that correct some bug on the linking and make the hack bellow easier, they are may be not all usefull but i worked with them %%%%
\usepackage{footnotehyper}
\usepackage{footnotebackref}
\usepackage{tablefootnote}
%\usepackage[all]{hypcap} % not usefull but you can include it i guess
%\usepackage{afterpage} % afterpage could be usefull for defining footnotetext associated to big figure that take a full page

%%%%% Floatrow + perpage are necesserary to have a good numbering of the footnotes, etoolbox and refcount are needed too %%%%%%
\usepackage{etoolbox}
\usepackage{refcount}
\usepackage{floatrow} 

%%%% Perpage package (footnote reset on each page) + the hack to remove the bug introduce on the perpage package by the new versions of latex %%%
\usepackage{perpage} %the perpage package
\MakePerPage{footnote} %the perpage package command
\makeatletter
\@removefromreset{footnote}{chapter}
\makeatother

\usepackage[float=false]{scrhack} % small hack for compatibilité with koma-script, not critical. 

%%%%% Setup of Hyperref, do as you wish %%%%%%
\hypersetup{
pdfpagemode=FullScreen,% 
bookmarksnumbered=true,%
colorlinks=true, %
breaklinks=true, %
urlcolor= blue, %
linkcolor= red, %
citecolor=blue, %
pdftitle={Footnotemark the label way, demonstration}, %i
pdfauthor={Nils Beaussé}, %
pdfsubject={Footnotemark the label way, demonstration} %
pdfkeywords={Footnotemark, Footnotetext}
} 

%%%%%%%%%% LOT OF PACKAGES AND DEFINITIONS END %%%%%%%%%%

%%%%%%%%%% Utility commands %%%%%%%%%

%%% refsetcounter like refstepcounter %%%
\makeatletter
\newcommand*\refsetcounter[2]{\setcounter{#1}{#2}%
  \protected@edef\@currentlabel{\csname p@#1\endcsname\csname the#1\endcsname}%
  }
\makeatother


%%% Based on https://tex.stackexchange.com/questions/533647/how-to-make-a-robust-conditional-labeling-that-resist-multiple-compilation-and-f : allow robust testing of label existence through diverse environnement, including floatrow %%%
\makeatletter

\newcommand*{\my@MaybeDefine}[2]{%
  \ifcsundef{my@goodvalue@#1}{\csgdef{my@goodvalue@#1}{#2}}{}%
}

\newcommand*{\my@WriteCtr}[2]{%
  \write\@auxout{\string\my@MaybeDefine{#1}{#2}}%
}

\newcommand*{\ConditionnalRobustLabel}[3]{%
  \ifx\protect\@typeset@protect% On rentre ici seulement si on est en mode typesetting (la fin pour tout le monde sauf pour floatrow)
	\ifcsdef{c@mycount@#1}{%
	\stepcounter{mycount@#1}% On augmente le conteur mycount de ce label à chaque appelle de mylabel (à chaque fois qu'il est traité en mode typesetting)	
	}{%
	\newcounter{mycount@#1}%
	\stepcounter{mycount@#1}%
	}%
	\edef\my@internal@label{my@internal@label@#1@\number\value{mycount@#1}}%
    \ifcsdef{my@goodvalue@#1}% Si mygoodvalue existe (a priori : c'est qu'on est bien là ou il faut)
      {\ifnum\value{mycount@#1}=\csuse{my@goodvalue@#1}%
        %\refstepcounter{#2}%
		\label{#1}%
		#2%
       \else%
			\IfRefUndefinedBabel{#1}{}{% Ref #1 is defined
           		\IfRefUndefinedBabel{\my@internal@label}%
             		{}%
             		{%
              			 \ifnum\getpagerefnumber{\my@internal@label}=\getpagerefnumber{#1}%
                		#3%
              			 \else%
                  		 	\ifnum\getpagerefnumber%
                           		{\my@internal@label}>\getpagerefnumber{#1}%
                     			#3%
                   			\fi%
               			\fi%
             		}%
         		}%     
       	\fi%
      }%
      {\typeout{You need to rerun LaTeX for the special labels.}}%
      \label{\my@internal@label}%
    \begingroup% On écrit my@goodvalue avec la valeur du nombre de fois qu'on a été typesetté mais seulement au dernier typesetting ! 
      \edef\tmp{\endgroup\noexpand\my@WriteCtr{#1}{\number\value{mycount@#1}}}%
    \tmp% Apelle de my@WriteCtr avec le label
  \fi%
}%
\makeatother

%%%%%%%%%% End of Utility commands %%%%%%%%%

%%%%%%%%%% Work on the command footnote for backref etc... %%%%%%%%
\let\oldfootnote\footnote
%\renewcommand{\footref}[1]{\,\oldfootref{#1}}

%a new counter to create an unambiguous label-tag
\newcounter{myHyperFootnoteCounter}

\makeatletter

% branch between the footnote with/without opt. argument
\renewcommand{\footnote}{\@ifnextchar[\my@OptHyperFootnote\my@HyperFootnote}

%define an new footnote without optional argument
\newcommand{\my@HyperFootnote}[1]{%
    \refstepcounter{myHyperFootnoteCounter}%
    \def\myFootnoteTag{hfn:\themyHyperFootnoteCounter}%
    \label{\myFootnoteTag}%
    \oldfootnote{\hyperref[\myFootnoteTag]{$\uparrow$}~#1}%
}

%define an new footnote with optional argument
\newcommand{\my@OptHyperFootnote}[2][plop]{%
    \refstepcounter{myHyperFootnoteCounter}%
    \def\myFootnoteTag{hfn:\themyHyperFootnoteCounter}%
    \label{\myFootnoteTag}%
    % put the optional argument to the original `footnote`
    \oldfootnote[#1]{\hyperref[\myFootnoteTag]{$\uparrow$}~#2}% 
}

%%%%%%%%%% New footref command that works in any situation and compatible with hyperref etc... %%%%%%%%%
\makeatletter
\newcommand\footnoteref[1]{\protected@xdef\@thefnmark{\ref{#1}}\@footnotemark}
\makeatother
%
% Footref is not taken into account by Babel and so have different spacing between text and footnotemark, here we use thefootnotespaceN defined in the babel hack to make it work with babel %
\let\oldfootref\footref
\renewcommand{\footref}[1]{\thefootnotespaceN\oldfootref{#1}}
\newcommand\footrefwithoutspace[1]{\oldfootref{#1}}

%%%%%%%%%%% BEGENNING OF THE WORK ON FOOTNOTEMARK/FOOTNOTETEXT %%%%%%%%%%

\makeatletter

%%%%%% BEGENNING OF THE WORK ON FOOTNOTEMARK/FOOTNOTETEXT without [] %%%%%

\newcounter{myHyperFootnoteCounterMARK} % For footnotemark without [] a simple global counter is enough
\newcounter{conteur_temporaire_footnote}
\newcounter{lalala}

\let\oldfootnotetext\footnotetext
\let\oldfootnotemark\footnotemark
%
\renewcommand{\footnotetext}{\@ifnextchar[\myOptHyperFootnotetext\my@HyperFootnotetext}
%
\newcommand{\my@HyperFootnotetext}[1]{%
    \def\myFootnoteTagtext{hfu:\themyHyperFootnoteCounterMARK}%
\oldfootnotetext{\hyperref[\myFootnoteTagtext]{$\uparrow$}~#1}
}

\renewcommand{\footnotemark}{\@ifnextchar[\myOptHyperFootnotemark\my@HyperFootnotemark}%
%
\newcommand{\my@HyperFootnotemark}{%
    \refstepcounter{myHyperFootnoteCounterMARK}%
    \def\myFootnoteTagtext{hfu:\themyHyperFootnoteCounterMARK}%
    \label{\myFootnoteTagtext}%
\oldfootnotemark%
}

%%%%%% BEGENNING OF THE WORK ON FOOTNOTEMARK/FOOTNOTETEXT with [] %%%%%

%%% we defined a personnal version of the footnotemark code, we use refstepcounter to allow the work of the label/hyperref mechanism and we transmit the two new argument to @myfootnotemark %%%%
\def\mymyfootnotemark #1#2{%
\refstepcounter{footnote}%
\protected@xdef\@thefnmark{\thefootnote}%
\@myfootnotemark{#1}{#2}%
}

%%% We defined a personnal version of the hyperref code for footnotemark, we use the hyperref command \hyperlink or \hyperref with the optionnal argument instead of the structure of hyperef : the idea is that hyperlink/hypertarget/hyperref allow crossreferencing but the classical hyperref version implemented in footnotemark don't (why hypperef don't use its own cross-reference compatible command .... a mystery ... . %%%%%

% Code of footnotemark copy paste and modified from hyperref code, we use refstepcounter for the same reason that in mymyfootnotemark and we use the hyperref mechanism directly (or the \hypertarge/thyperlink but the anchor in this case is set not far bellow the line, which is not very good) %
\newcommand{\@myfootnotemark}[2]{%
    \leavevmode%
    \ifhmode\edef\@x@sf{\the\spacefactor}\nobreak\fi%
    \refstepcounter{Hfootnote}%
    \global\let\Hy@saved@currentHref\@currentHref%
    \hyper@makecurrent{Hfootnote}%
    \global\let\Hy@footnote@currentHref\@currentHref%
    \global\let\@currentHref\Hy@saved@currentHref%
    %\hyperlink{#1}{\hypertarget{#2}{\@makefnmark}} % uncomment this for the hyperlink/hypertarget mode : more precise but go ont line bellow ... (don't know why)
    \label{#2}% For the backreference
    \hyperref[#1]{\@makefnmark} % For the reference
    \ifhmode\spacefactor\@x@sf\fi%
    \relax%
  }%
\makeatother

%%% Counter for saving temporary value unique ID for the footnotemark and footnotetext without [] %%%%%%%

%%% Counter for saving temporary value in myOptHyperFootnotetext, could be create locally but it work %%%%%%%
\newcounter{counteur_temporary_footnote}
\newcounter{counteur_temporary_Hfootnote}

%%%%% define an new footnotemark/footnotetext with optional argument that will act like a label instead of the classical footnotemark[number] : implement diverses tricks from the hyperref manual and from the web to allow proper backreferencing-hyperlink and standard hyperlink outside and inside figure, floatrow and other environnement %%%%%

\makeatletter

%%%% The footnotetext[] part %%%%
\newcommand{\myOptHyperFootnotetext}[2][plop]{
    \def\myFootnoteTagtextARGU{hfoi:#1}%
    \def\myFootnoteTagtextARGUU{hfoz:#1}%
% 
	\ifcsname counter@Href:#1\endcsname
	% CASE DEFINED
		\expandafter\let\expandafter\@plip\csname counter@Href:#1\endcsname%
		\expandafter\let\expandafter\@plipp\csname counterr@Href:#1\endcsname%
		\setcounter{counteur_temporary_footnote}{\value{footnote}}%
		\setcounter{counteur_temporary_Hfootnote}{\value{Hfootnote}}%
		\refsetcounter{footnote}{\@plip}%
		\refsetcounter{Hfootnote}{\@plipp}%
		%
		\oldfootnotetext{\phantomsection\label{\myFootnoteTagtextARGUU}\hyperref[\myFootnoteTagtextARGU]{$\uparrow$}~#2\label{#1}}%
		%\footnotetext{\hyperlink{\myFootnoteTagtextARGU}{$\uparrow$}\hypertarget{\myFootnoteTagtextARGUU}{~#2}\label{#1}}%Uncomment this for the fyperlink/hypertarget version : more precise but go one line bellow, don't know why...
		%	
		\refsetcounter{footnote}{\value{counteur_temporary_footnote}}%
		\refsetcounter{Hfootnote}{\value{counteur_temporary_Hfootnote}}%
	\else 
		%nothing to do : wait that it is defined by a footonotemark and reexecute at the next compilation : maybe should add a warning to rerun latex here
	\fi
}
\makeatother
%

%%%% The footnotemark[] part %%%%
\makeatletter
\newcommand{\myOptHyperFootnotemark}[1][plop]{%
	\def\myFootnoteTagtextargu{hfoi:#1}%
    \def\myFootnoteTagtextarguu{hfoz:#1}%
%	\def\mylabeltest{labeltest@Href@#1}%
%	\def\mylabeltestt{r@labeltest@Href@#1}%
	%
	\ifcsname counter@Href:#1\endcsname%
	% CASE DEFINED
			\ConditionnalRobustLabel{labeltesttest@Href@#1}{\mymyfootnotemark{\myFootnoteTagtextarguu}{\myFootnoteTagtextargu}}{\footref{#1}}{}%ConditionnalRobustLabel is mandatory here to allow the switch between the classic footnotemark and footref in all situation, including in special environnement like floatrow
		%	
		% Don't know why, but without this last uncoherent lines, this cause glitch...
		\expandafter\xdef\csname counter@Href:#1\endcsname{\arabic{footnote}}% 
		\expandafter\xdef\csname counterr@Href:#1\endcsname{\arabic{Hfootnote}}%
		%For second execution of latex, we store the value of the footnote (case where footnotetext is 		called before footnotemark).
		\immediate\write\@auxout{\noexpand\expandafter\noexpand\xdef\noexpand\csname counter@Href:#1\endcsname{\arabic{footnote}}}%
		\immediate\write\@auxout{\noexpand\expandafter\noexpand\xdef\noexpand\csname counterr@Href:#1\endcsname{\arabic{footnote}}}%	
	\else%
	% CASE UNDEFINED
		\mymyfootnotemark{\myFootnoteTagtextarguu}{\myFootnoteTagtextargu}%
		\expandafter\xdef\csname counter@Href:#1\endcsname{\arabic{footnote}}% 
		\expandafter\xdef\csname counterr@Href:#1\endcsname{\arabic{Hfootnote}}%
		%For second execution of latex, we store the value of the footnote (case where footnotetext is called before footnotemark).
		\immediate\write\@auxout{\noexpand\expandafter\noexpand\xdef\noexpand\csname counter@Href:#1\endcsname{\arabic{footnote}}}%
		\immediate\write\@auxout{\noexpand\expandafter\noexpand\xdef\noexpand\csname counterr@Href:#1\endcsname{\arabic{footnote}}}%
	\fi	
	%	
}%
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %%%%%%%%%% Babel compatibility fix %%%%%%%%%%%%%%
\makeatletter
%%% copy-paste from the babel code for hacking footnotemark %%%
\ifdefined\frenchbsetup
\AtBeginDocument{
\@ifpackageloaded{bigfoot}%
                   {\PackageInfo{french.ldf}%
                     {bigfoot package in use.\MessageBreak
                      babel-french will NOT customise footnotes;%
                      \MessageBreak reported}}%
                   {\let\@footnotemarkORII\@myfootnotemark%we change the name
                    \def\@footnotemarkFBI{\leavevmode\unskip\unkern
                                         \,\@footnotemarkORII}%we change the name
                    \ifFBAutoSpaceFootnotes
                      \let\@myfootnotemark\@footnotemarkFBI%And applied it to our version of footnotemark
                    \fi}%
                    \csgdef{thefootnotespaceN}{\,}%In addition : for footnoteref, same type off space saved.
}
\else
					\AtBeginDocument{\csgdef{thefootnotespaceN}{}}%in other case we return to english case : no space. 
\fi 


\makeatother


\title{Demonstration label-like footnotemark / footnotetext}           % Les paramètres du titre : titre, auteur, date
\author{Nils Beaussé}

%\usepackage[perpage]{footmisc}

%%%%%%% some package %%%%%%%
\KOMAoptions{DIV=last}

%%%%%% begin %%%%%%%
\begin{document}
%%%%%% TEST %%%%%%

\chapter{TEST}
\section{Introduction}

A classical footnote\footnote{a classical footnote}
A classical footnotemark footnotetext without []\footnotemark
\footnotetext{classical footnotemark/footnotetext}
The new footnotemark[] with footnotetext defined after\footnotemark[yap] and again here\footnotemark[oup].
\footnotetext[yap]{first footnote defined before}
\footnotetext[oup]{first footnote defined after}

\footnotetext[bloup]{Footnotetext defined before}
The new footnotemark[] with footnotetext defined before if you want\footnotemark[bloup]

\begin{figure}[h]
\centering
\includegraphics[scale=0.2]{example-image-b}
\caption[test]{footnotemark[] in figure\footnotemark[thing] footnotemark[] again\footnotemark[thingg]}%
\label{Tux}
\end{figure}
\footnotetext[thing]{A first footnote from a figure (defined after)}%
\footnotetext[thingg]{A second footnote from a figure}%

%\footnotetext{A footnote}
%\footnotetext{A footnote}

%%%%%%%% The code calling from the caption inside a floatrow %%%%%%%%
\footnotetext[truc]{A floatrow footnote defined before}% 
\begin{figure}[ht]
   \centering
   {
     \begin{floatrow}[1]
        \ffigbox[\FBwidth]{\caption[Floatrow footnotemark]{\label{Tino}Floatrow footnotemark[]\footnotemark[truc]}}{\includegraphics[scale=0.4]{example-image-a}}
     \end{floatrow}
   }
\end{figure}

A classical footnote again with a label for footref\footnote{\label{yolo}yolo}
A classical footnotemark/footnotetext with a label\footnotemark ! 
\footnotetext{\label{yili}footnotetext classical again}

Or footref to the classical footnote\footref{yolo}
A footref to the classical footnotetext\footref{yili}
A footref to the footnotemark[] in the floatrow\footref{truc}

\end{document}


Limitation 1 :
For now there are no reorganizing in footnote. So even if the footnotemark number always match the footnotetext number, the order of footnotetext in the footnote section of the document still depend of the order of the footnotetext command. Even if footnotetext can be called everywhere in practical like :
footnotetext[foo]
footnotemark[foo]

Footnotemark[baa]
footnotetext[baa]


A called like :
footnotetext[baa]{second footnote}
footnotemark[foo]%first footnote

Footnotemark[baa]%second footnote
footnotetext[foo]{firstfootnote}

will give :

2 : second footnote
1 : first footnote

With a good numbering but a wrong order.

At this point of my use this is not a real problem, but later i think i could extract the automatic reorganisation of footnote/list/whatever in some package to put this in good order.

I guess with some other tricks it should be possible to hack figure and other environnement to add at the end an automatic insertion of the footnotetext and make a general footnote command which allow everything in all environnement, but it is too much work for me at this point ;)

Limitation 2 :
Due to the usage of footref, the code need Koma-script classes. I guess it should be possible to copy-paste the footref definition of koma-script and to make a conditionnal switch to use the one of Komascript of the other, but i have no time for this right now.
An other idea is to delete the footref reference, but a footnotemark[foo] footnotemark[foo] will give an empty result for the second one.

Limitation 3 :
I have not tested it with the « multiple » option of koma-script. I have based my footnote code on the simple version, it is easy to copy-paste the other version in the komascript code and to make a switch in this code with it, but i have no time for this neither :p

Limitation 4 :
Due to the nature of tricks used in the code, it requires a loooooooooooooot of compiling to stabilise. A new file require 5/6 compilations to stabilise.

I guess that other solution could work or that some package does the same job, but i didn't find any reliable solutions in my search, the most of them are incompatible with hyperref for example or just put the footnote bellow the environnement, or are specialised for one environnement (tablefootnote) and don't work in others, here i have tried a more general approach...

You are free to use it as you wish ;)

Recommended reading 2021:

LaTeXguide.org • LaTeX-Cookbook.net
LaTeX Beginner's Guide LaTeX Cookbook
Nilsou
Posts: 2
Joined: Tue Mar 24, 2020 4:12 pm

A footnote/footnotemark solution to make it compatible with figure/floatrow/hyperref and backreference

Postby Nilsou » Tue Sep 06, 2022 5:07 pm

Quick update, you can find here :
https://mega.nz/file/eHYRDCKS#lwcN0cA-Y ... MhhXJuSbpE

The minimum example + the code shaped into a package sty.

Enjoy.


Return to “Page Layout”

Who is online

Users browsing this forum: No registered users and 1 guest