5 目录中未编号的章标题后面取消点和页码,而正文的章标题也就是有编号的章标题仍然保留点和页码,请问如何实现?

发布于 2024-08-16 00:53:52

我的MWE是这样的


\documentclass[12pt,b5paper,twoside]{book}
\usepackage{titlesec,xeCJK,zhlipsum,fontspec}
\usepackage[margin=2.5cm]{geometry}
\setcounter{tocdepth}{1}
\renewcommand{\normalsize}{\fontsize{10.5pt}{14pt}\selectfont}
\titleformat{\chapter}{\centering\fontsize{16pt}{16pt}\sffamily}{\fontsize{16pt}{16pt}\selectfont 第 \thechapter 章}{1em}{}
\titlespacing{\chapter}{0pt}{2cm}{20pt}


\renewcommand{\contentsname}{目 \quad 录}
\usepackage[titles]{tocloft}
\renewcommand{\cftchapnumwidth}{5em}
\renewcommand{\cftdot}{\ensuremath{\cdot}}
\renewcommand{\cftdotsep}{2}
\renewcommand{\cftchapleader}{\cftdotfill{\cftchapdotsep}}
\renewcommand{\cftchapdotsep}{\cftdotsep} 
\renewcommand{\cftchappresnum}{第\ }
\renewcommand{\cftchapaftersnum}{章\ }

\renewcommand{\cftsecindent}{2em}
\renewcommand{\cftsecnumwidth}{2.5em}
\renewcommand{\cftsecpresnum}{\S\ }
\renewcommand{\cftsecaftersnum}{.}
\begin{document}
    
    \setlength{\parindent}{2em}
    \pagenumbering{Roman}
    \chapter*{前序}
    \zhlipsum[1]
    \addcontentsline{toc}{chapter}{前言}
    \tableofcontents
    
    \clearpage
    \pagenumbering{arabic}
    
    \chapter{概述}
    \section{diyi}
    \zhlipsum[2]
\end{document}

编译后的效果如下图显示:

contents.png

需求/目标
目录中未编号的章标题,比如前言后面取消点和页码,而正文的章标题也就是有编号的章标题,例如第1章仍然保留点和页码。

查看更多

关注者
0
被浏览
723
Eureka
Eureka 2024-08-16
这家伙很懒,什么也没写!

1. 思路

根据 tocloft 的官方文档,我们可以知道所谓的那一系列的 leader 是由命令 \cftdotfill 控制的, 此命令的声明如下:

\providecommand{\cftdotfill}[1]{%
  \def\@tempa{#1}%
  \def\@tempb{\cftnodots}%
  \ifx\@tempa\@tempb
    \hfill
  \else
    \ifStartUnumToc
      \hfill\StartUnumTocfalse
    \else
      \leaders\hbox{$\m@th\mkern #1 mu\hbox{\cftdot}\mkern #1 mu$}\hfill
    \fi
  \fi
}

其中的 #1 就是你指定的那个 \cftXdotsep, 这里的 X 就是 chap. 我们可以看到里面有一个比较操作 \ifx\@tempa\@tempb, 这里其实是比较当前传入的 \cftXdotsep\cftnodots, 查看 tocloft.sty 文档,可以发现其生命如下:

% around line 167
\newcommand{\cftnodots}{10000}

所以现在的思路就来了,只要我们在使用 \chapter* 前,让这个 \ifxtrue 对应的分支,那么就可以取消 ToC 中对应条目后面的 leader 了. 所以只需要把对应的 \cftXdotsep 设置的足够大即可.

我为什么回去找这个 \cftdotfill? 因为官方文档中有这样的说明: "However, if the separation is too large then no dots will be actually typeset. The macro cftnodots is a separation value that is ‘too large’"

2. patch command

下面就来说怎么实现上面的目标,我个人喜欢 patch 的方式,当然,你也可以采用 renewcommand 的方式. 所以这里我引入了 etoolbox 宏包用于在 \chapter*\chapter 命令前设置这两个 \cftXdotsep. 如果你读过 source2e 应该知道,一般的 star-form 的命令和对应的原始命令是通过如下的方式生命的:

\def\foo{\@ifstar\@foo\@@foo}
\def\@foo#1{...}
\def\@@foo#1{...}

所以我们这里要 patch 的命令其实是 \@schapter\@chapter,分别对应 star-form 和 original-form. 但是我们这里要把这些 \cftXdotsep 的重定义注入到 .toc 文件中. (你可以想一想为什么要注入到 .toc 文件,而不是直接放在你的 .tex 文件中?). 所以我们 patch 部分的代码为:

% ==> start patch
\usepackage{etoolbox}
\makeatletter
% 1. cancel dot line
\def\chapter@CancelLeader#1{
  \write\@auxout{%
    \protect\@writefile{toc}{%
      \protect\renewcommand{\protect\cftchapdotsep}{#1}%
    }%
  }%
}
\pretocmd{\@schapter}{\chapter@CancelLeader{10000}}{}{}
\pretocmd{\@chapter}{\chapter@CancelLeader{2}}{}{}
\makeatother

patch 后的 .toc 文件大概长这样:

\renewcommand {\cftchapdotsep }{10000}
\contentsline {chapter}{前序}{I}{}%
\renewcommand {\cftchapdotsep }{10000}
\renewcommand {\cftchapdotsep }{2}
\contentsline {chapter}{\numberline {1}概述}{1}{}%
\contentsline {section}{\numberline {1.1}diyi}{1}{}%
\renewcommand {\cftchapdotsep }{2}
\contentsline {chapter}{\numberline {2}Test I}{3}{}%
\contentsline {section}{\numberline {2.1}sse I}{3}{}%
\contentsline {section}{\numberline {2.2}sse II}{3}{}%
\renewcommand {\cftchapdotsep }{10000}
\contentsline {chapter}{Test II}{5}{}%
\contentsline {section}{\numberline {2.3}sse I}{5}{}%
\contentsline {section}{\numberline {2.4}sse II}{5}{}%

3. mwe

最终修改后的代码为:

\documentclass[12pt,b5paper,twoside]{book}
\usepackage{titlesec,xeCJK,zhlipsum,fontspec}
\usepackage[margin=2.5cm]{geometry}


\setcounter{tocdepth}{1}
\renewcommand{\normalsize}{\fontsize{10.5pt}{14pt}\selectfont}
\titleformat{\chapter}{\centering\fontsize{16pt}{16pt}\sffamily}{\fontsize{16pt}{16pt}\selectfont 第 \thechapter 章}{1em}{}
\titlespacing{\chapter}{0pt}{2cm}{20pt}
\renewcommand{\contentsname}{目 \quad 录}
\usepackage[titles]{tocloft}
\renewcommand{\cftchapnumwidth}{5em}
\renewcommand{\cftdot}{\ensuremath{\cdot}}
\renewcommand{\cftdotsep}{2}
\renewcommand{\cftchapleader}{\cftdotfill{\cftchapdotsep}}
\renewcommand{\cftchapdotsep}{2} 
\renewcommand{\cftchappresnum}{第\ }
\renewcommand{\cftchapaftersnum}{章\ }
\renewcommand{\cftsecindent}{2em}
\renewcommand{\cftsecnumwidth}{2.5em}
\renewcommand{\cftsecpresnum}{\S\ }
\renewcommand{\cftsecaftersnum}{.}


% ==> start patch
\usepackage{etoolbox}
\makeatletter
\def\chapter@CancelLeader#1{
  \write\@auxout{%
    \protect\@writefile{toc}{%
      \protect\renewcommand{\protect\cftchapdotsep}{#1}%
    }%
  }%
}
\pretocmd{\@schapter}{\chapter@CancelLeader{10000}}{}{}
\pretocmd{\@chapter}{\chapter@CancelLeader{2}}{}{}
\makeatother


\begin{document}
  \setlength{\parindent}{2em}
  \pagenumbering{Roman}
  \chapter*{前序}
  \zhlipsum[1]
  \addcontentsline{toc}{chapter}{前序}
  \tableofcontents
  
  \clearpage
  \pagenumbering{arabic}
  \chapter{概述}
  \section{diyi}
  \zhlipsum[2]

  \chapter{Test I}
  \section{sse I}
  \section{sse II}

  
  \chapter*{Test II}
  \addcontentsline{toc}{chapter}{Test II}
  \section{sse I}
  \section{sse II}
\end{document}

对应的编译结果如下:

image.png

4. page number

我觉得\chapter* 对应 ToC 项后面的 page number 后看起来并不是那么的美观, 但是既然你在问题里面要求了,我还是把对应的 patch 代码补充在这里吧。把以下的代码放到导言区即可:

%\usepackage{etoolbox}
\makeatletter
% 2. cancel page number
\newif\ifChapter@Num
\def\addcontentsline#1#2#3{%
  \addtocontents{#1}{\protect\contentsline{#2}{#3}{\ifChapter@Num\thepage\fi}{}%
  \protected@file@percent}
}
\pretocmd{\@schapter}{\Chapter@Numfalse}{}{}
\pretocmd{\section}{\Chapter@Numtrue}{}{}
\pretocmd{\@chapter}{\Chapter@Numtrue}{}{}
\makeatother

加上这个 patch 后的编译效果如下:
image.png

1 个回答

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览