There are several issues with you first approach.
First of all, even the original definition of book.cls
does not have:
\addcontentsline{toc}{chapter}{\numberline {#1}}
in the definition of \@chapter
, but:
\addcontentsline{toc}{chapter}{\protect\numberline{\thechapter}#1}
So there is a \protect
missing and the correct argument to \numberline
and the first argument of \@chapter
is used without braces.
Second problem is that after loading hyperref
the definition of \@chapter
changes. So the patch must be before loading hyperref
.
BTW: To see the original definition, just add \show\@chapter
before \xpatchcmd
. This will output the current definition before patching \@chapter
in the log
-file.
Third problem is, that the replacement:
\addcontentsline{toc}{chapter}{\protect\numberline {Chapter~\thechapter{}.~#1}}
Would put not only Chapter~<number>.
into the number argument but also the title. So it should be at least:
\addcontentsline{toc}{chapter}{\numberline {Chapter~\thechapter{}.}#1}
But for this definition you would also need a change of \l@chapter
to increase the space reserved for the number. Maybe instead you don't want to reserve number space and instead like to use:
\addcontentsline{toc}{chapter}{Chapter~\thechapter{}.~#1}
So with:
\documentclass{book}
\usepackage{xpatch}
\makeatletter
\xpatchcmd{\@chapter}
{\addcontentsline{toc}{chapter}{\protect\numberline{\thechapter}#1}}
{\addcontentsline{toc}{chapter}{Chapter~\thechapter{}.~#1}}
{\typeout{Success}}
{\typeout{Failed!}}
\makeatother
\usepackage{hyperref}
\begin{document}
\tableofcontents
\chapter{This is First}
Some day this will work.
\chapter{This is Second}
Perhaps soon!
\end{document}
you get:
The alternative with using Chapter <number>.
as argument of \numberline
would be, e.g.:
\documentclass{book}
\usepackage{xpatch}
\makeatletter
\xpatchcmd{\@chapter}
{\addcontentsline{toc}{chapter}{\protect\numberline{\thechapter}#1}}
{\addcontentsline{toc}{chapter}{\protect\numberline{Chapter~\thechapter{}.}#1}}
{\typeout{\string\@chapter\space patch success}}
{\typeout{\string\@chapter\space patch failed!}}
\xpatchcmd{\l@chapter}
{\setlength\@tempdima{1.5em}}
{\setlength\@tempdima{6em}}% increase reserved number width
{\typeout{\string\l@chapter\space patch success}}
{\typeout{\string\l@chapter\space patch failed!}}
\makeatother
\usepackage{hyperref}
\begin{document}
\tableofcontents
\chapter{This is First}
Some day this will work.
\chapter{This is Second}
Perhaps soon!
\end{document}
The result for one line entries is similar to the first suggestion. To see a difference try entries with more than one line, e.g.
\chapter{This is Second Chapter with very long Titel Needing more than one
Line in the Table of Contents}
So IMHO this second approach of mine is, what you want.
Your problem with your second approach is, that you don't take the dot .
and the space ~
after the number into account, when increasing \cftchapnumwidth
. Here one possible correction of your setting:
\documentclass{book}
\usepackage{tocloft}
\begin{document}
\newlength{\tocchapwidth}
\renewcommand{\cftchappresnum}{\chaptername\ }
\settowidth{\tocchapwidth}{\cftchappresnum}
\renewcommand{\cftchapaftersnum}{.~}
\newlength{\tocchapafternumwidth}
\settowidth{\tocchapafternumwidth}{\cftchapaftersnum}
\addtolength{\cftchapnumwidth}{\tocchapwidth}
\addtolength{\cftchapnumwidth}{\tocchapafternumwidth}
\renewcommand{\cftchapdotsep}{\cftdotsep}
\tableofcontents
\chapter{This is First}
Some day this will work.
\chapter{This is Second, a Rather Long and Windy Road Leading to a Door Going to Nowhere Fast}
Perhaps soon!
\end{document}
But if you have a intense look into this suggestion it is still not correct, because you are measuring text in the current document font not in the font used for the chapter entries. So the previous code only seems to be correct, but it isn't. For correct code you have to take the \cftchapfont
into account, when measuring the text widths. (And now you don't need to increase the width also by the space, if the number width without the prefix Chapter
and the postfix .
was already enough). So better code would be something like:
\documentclass{book}
\usepackage{tocloft}
\newlength{\tocchapwidth}
\newlength{\tocchapafternumwidth}
\renewcommand{\cftchappresnum}{\chaptername\ }
\renewcommand{\cftchapaftersnum}{.}
\renewcommand{\cftchapdotsep}{\cftdotsep}
\AtBeginDocument{% final font setup is done in \begin{document} so we delay all text measuring
\settowidth{\tocchapwidth}{\cftchapfont\cftchappresnum}%
\settowidth{\tocchapafternumwidth}{\cftchapfont\cftchapaftersnum}%
\addtolength{\cftchapnumwidth}{\tocchapwidth}%
\addtolength{\cftchapnumwidth}{\tocchapafternumwidth}%
}
\begin{document}
\tableofcontents
\chapter{This is First}
Some day this will work.
\chapter{This is Second, a Rather Long and Windy Road Leading to a Door Going to Nowhere Fast}
Perhaps soon!
\end{document}
The Appendix Problem
Note: Both examples are not suitable for documents with appendix, because in this case Chapter
wouldn't be the correct term in the appendix.
Making the recommended suggestion without tocloft
suitable for documents with appendix would be easy. You just need to replace the hard-coded Chapter
by \@chapapp
and increase the width:
\documentclass{book}
\usepackage{xpatch}
\makeatletter
\xpatchcmd{\@chapter}
{\addcontentsline{toc}{chapter}{\protect\numberline{\thechapter}#1}}
{\addcontentsline{toc}{chapter}{\protect\numberline{\@chapapp~\thechapter{}.}#1}}
{\typeout{\string\@chapter\space patch success}}
{\typeout{\string\@chapter\space patch failed!}}
\xpatchcmd{\l@chapter}
{\setlength\@tempdima{1.5em}}
{\setlength\@tempdima{7em}}% increase reserved number width
{\typeout{\string\l@chapter\space patch success}}
{\typeout{\string\l@chapter\space patch failed!}}
\makeatother
\usepackage{hyperref}
\begin{document}
\tableofcontents
\chapter{This is First}
Some day this will work.
\chapter{This is Second}
Perhaps soon!
\appendix
\chapter{This is a Rather Long and Windy Road Leading to a Door Going
to Nowhere Fast}
\end{document}
For the tocloft
solution you could use something like:
\documentclass{book}
\usepackage{tocloft}
\newlength{\tocchapwidth}
\newlength{\tocappwidth}
\newlength{\tocchapafternumwidth}
\renewcommand{\cftchappresnum}{\chaptername\ }
\renewcommand{\cftchapaftersnum}{.}
\renewcommand{\cftchapdotsep}{\cftdotsep}
\AtBeginDocument{%
\settowidth{\tocchapwidth}{\cftchapfont\chaptername}%
\settowidth{\tocappwidth}{\cftchapfont\appendixname}%
\ifdim\tocappwidth>\tocchapwidth
\addtolength{\cftchapnumwidth}{\tocappwidth}%
\else
\addtolength{\cftchapnumwidth}{\tocchapwidth}%
\fi
\settowidth{\tocchapafternumwidth}{\cftchapfont\cftchapaftersnum}%
\addtolength{\cftchapnumwidth}{\tocchapafternumwidth}%
}
\AddToHook{cmd/appendix/after}{%
\addtocontents{toc}{%
\protect\renewcommand{\protect\cftchappresnum}{\protect\appendixname{} }%
}%
}
\begin{document}
\tableofcontents
\chapter{This is First}
Some day this will work.
\chapter{This is Second, a Rather Long and Windy Road Leading to a Door Going to Nowhere Fast}
Perhaps soon!
\appendix
\chapter{A Rather Long Appendix Heading and Windy Road Leading to a Door Going to Nowhere Fast}
\end{document}