情况
我打算在类似“实验室手册”的乳胶文档中做一些笔记(比较这些模板1,2)。该议定书将跨越很长的时间,并且条目可能从几个项目到几个方面不等。
为了更好地记录我什么时候做了什么,我想有一个日历(例如这或者这)显示章节标题(可能还有页码)。这些应该链接到各个部分(就像图片那样)这里)。
显然,可以使用 hyperref 并简单地手动执行。然而,以自动生成的方式生成目录是更可取的。
问题
我如何创建看起来像日历的目录?
(或:像目录一样的日历)
(可能不是最小的)工作示例
看起来不是特别好看,创建起来也不是很快捷(即使我使用了自定义命令来输入):
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Monthly Calendar
% LaTeX Template
%
% This template has been downloaded from:
% http://www.latextemplates.com
%
% Original calendar style author:
% Evan Sultanik (http://www.sultanik.com/LaTeX_calendar_style)
%
% Important note:
% This template requires the calendar.sty file to be in the same directory as the
% .tex file. The calendar.sty file provides the necessary structure to create the
% calendar.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Modified in order to represent the MWE for tex.stackexchange question:
% https://tex.stackexchange.com/q/356889/74942
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%----------------------------------------------------------------------------------------
% PACKAGES AND OTHER DOCUMENT CONFIGURATIONS
%----------------------------------------------------------------------------------------
\documentclass[a4paper]{article}
%
\usepackage{filecontents}
\begin{filecontents}{calendar.sty}
\NeedsTeXFormat{LaTeX2e}
\def\CalendarVersion{3.1}
\def\CalendarVersionDate{2009/04/24}
\ProvidesClass{calendar}[\CalendarVersionDate
LaTeX2e class file `calendar' v\CalendarVersion]
\typeout{'calendar' style \CalendarVersion \CalendarVersionDate.}
\typeout{Created by Evan Sultanik}
\typeout{****** Bugs/comments/suggestions/technicalities to Evan Sultanik -- [email protected] ******}
\RequirePackage{tabularx}
\def\@CALtrue{1}
\newcount\@currentdaynum
\newcounter{calendardate}
\newcount\StartingDayNumber
\def\dayheader#1#2{
\noindent
\begin{minipage}[t]{0.87\hsize}
\noindent
\raggedright
\textit{#1}
\end{minipage}
\begin{minipage}[t]{0.1\hsize}
\noindent
\raggedleft
\textit{#2}
\end{minipage}
}
\def\activities#1{
\parbox{\hsize}{\vspace*{5pt}\raggedright\scriptsize #1}\smallskip
}
\let\@colbreak=&
\def\printdayname#1{\hfil\textsc{#1}\hfil}
\newcommand{\dayname}[1]{
\ifnum#1=1 Sunday\else
\ifnum#1=2 Monday\else
\ifnum#1=3 Tuesday\else
\ifnum#1=4 Wednesday\else
\ifnum#1=5 Thursday\else
\ifnum#1=6 Friday\else
\ifnum#1=7 Saturday\else
\PackageError{calendar}{Unrecognized day number: #1!}
\fi\fi\fi\fi\fi\fi\fi
}
\StartingDayNumber=1
\newenvironment{calendar}[1]{
\newdimen\@calendarwidth
\@calendarwidth=#1
\begingroup
\def\@calendarmode{\@CALtrue}
\def\day##1##2{
\if\@calendarmode\@CALtrue\else\PackageWarning{calendar}{The '\day' macro is expected to be used in the 'calendar' environment!}\fi
\ifnum\@currentdaynum>7\global\@currentdaynum=1\fi
\global\advance\@currentdaynum by 1
\dayheader{##1}{\thecalendardate}\def\daysep{\vskip1pt\hrule\vskip1pt}
\activities{##2}
\addtocounter{calendardate}{1}
\ifnum\@currentdaynum>7\@arraycr\hline\else&\fi
}
\def\finishCalendar{
\ifnum\@currentdaynum=6 &\\\hline\else
\ifnum\@currentdaynum=5 &&\\\hline\else
\ifnum\@currentdaynum=4 &&&\\\hline\else
\ifnum\@currentdaynum=3 &&&&\\\hline\else
\ifnum\@currentdaynum=2 &&&&&\\\hline\else
\ifnum\@currentdaynum=1 &&&&&&\\\hline
\fi\fi\fi\fi\fi\fi
}
\def\BlankDay{
\if\@calendarmode\@CALtrue\else\PackageWarning{calendar}{The '\calendarday' macro is expected to be used in the 'calendar' environment!}\fi
\ifnum\@currentdaynum>7\global\@currentdaynum=1\fi
\global\advance\@currentdaynum by 1
\addtocounter{calendardate}{1}
\ifnum\@currentdaynum>7\@arraycr\hline\else&\fi
}
\setcounter{calendardate}{1}
\newcount\@currday
\@currday=\StartingDayNumber
\newcount\@numdays
\@numdays=7
\let\@cbreak=&
\tabularx{\@calendarwidth}{|X|X|X|X|X|X|X|} \hline
\ifnum\@currday>\@numdays\@currday=1\fi\printdayname{\dayname{\@currday}} \global\advance\@currday by 1 &
\ifnum\@currday>\@numdays\@currday=1\fi\printdayname{\dayname{\@currday}} \global\advance\@currday by 1 &
\ifnum\@currday>\@numdays\@currday=1\fi\printdayname{\dayname{\@currday}} \global\advance\@currday by 1 &
\ifnum\@currday>\@numdays\@currday=1\fi\printdayname{\dayname{\@currday}} \global\advance\@currday by 1 &
\ifnum\@currday>\@numdays\@currday=1\fi\printdayname{\dayname{\@currday}} \global\advance\@currday by 1 &
\ifnum\@currday>\@numdays\@currday=1\fi\printdayname{\dayname{\@currday}} \global\advance\@currday by 1 &
\ifnum\@currday>\@numdays\@currday=1\fi\printdayname{\dayname{\@currday}} \global\advance\@currday by 1 \\ \hline \hline
\@currentdaynum=1
\let\@firstline=\@CALtrue
}{
\endtabularx
\endgroup
}
\end{filecontents}
%
\usepackage{calendar} % Use the calendar.sty style
\usepackage[colorlinks=false,linkcolor=black,bookmarks=false]{hyperref}
\usepackage[margin=0.5in]{geometry}
\begin{document}
\pagestyle{empty} % Removes the page number from the bottom of the page
\noindent
\StartingDayNumber=1 % Calendar starting day, default of 1 means Sunday, 2 for Monday, etc
%----------------------------------------------------------------------------------------
% MONTH AND YEAR SECTION
%----------------------------------------------------------------------------------------
\setcounter{section}{-1}
\section{Table of Contents}
\begin{center}
\textsc{\LARGE january}\\ % Month
\textsc{\large 2017} % Year
\end{center}
%----------------------------------------------------------------------------------------
\begin{calendar}{\hsize}
%----------------------------------------------------------------------------------------
% BLANK DAYS BEFORE THE BEGINNING OF THE CALENDAR
%----------------------------------------------------------------------------------------
% This part is very finicky. It defines the number of blank days at the beginning of the calendar before the first of the month starts. If you need this to be more than 4 (i.e. the first starts on a Friday or Saturday in a 31 day month), then you have two options:
% 1) You can uncomment another one or two \BlankDay's below which will make a new week (6 total) which makes the calendar too big for one page, remedy this by decreasing the size of each day by replacing 2.5cm below with a smaller number.
% 2) Make the spill-over days start at the top left of the calendar (i.e. the calendar starts with 31 then a few days blank then 1, 2, 3, etc). The second option can be configured by uncommenting the below:
%\setcounter{calendardate}{31} % Begin the count with 31 so the top left day is 31; this can be changed to 29 or 30 as required
%\day{}{\vspace{2.5cm}} % 31 - add another line identical to this if starting at 30 or earlier
% You will need to comment out the 31 in the NUMBERED DAYS AND CALENDAR CONTENT section below for this as well as commenting out one of the \BlankDay's below. Play around with it and you will get it.
%\BlankDay
%\BlankDay
%\BlankDay
%\BlankDay
%\BlankDay
%\BlankDay
%----------------------------------------------------------------------------------------
% NUMBERED DAYS AND CALENDAR CONTENT
%----------------------------------------------------------------------------------------
% These are the numbered days in the template - if there are less than 31 days simply comment out the bottom lines.
% \vspace{2.5cm} is only there to provide an even look to the calendar where each day is 2.5cm tall, it can be changed or removed to automatically adjust to the day in the week with the most content
\setcounter{calendardate}{1} % Start the date counter at 1
\day{}{
\hyperref[01012017]{\nameref{01012017}} \hfill \pageref{01012017} \\
\hyperref[01012017_h]{\nameref{01012017_h}} \hfill \pageref{01012017_h} \\
\hyperref[01012017_s]{\nameref{01012017_s}} \hfill \pageref{01012017_s}
} % 1 - Example of content
\day{}{
\hyperref[02012017]{\nameref{02012017}} \hfill \pageref{02012017} \\
\hyperref[02012017_a]{\nameref{02012017_a}} \hfill \pageref{02012017_a} \\
\hyperref[02012017_b]{\nameref{02012017_b}} \hfill \pageref{02012017_b}
} % 2
\day{}{\vspace{2.5cm}} % 3
\day{}{\vspace{2.5cm}} % 4
\day{}{\vspace{2.5cm}} % 5
\day{}{\vspace{2.5cm}} % 6
\day{}{\vspace{2.5cm}} % 7
\day{}{\vspace{2.5cm}} % 8
\day{}{
\hyperref[09012017]{\nameref{09012017}} \hfill \pageref{09012017} \\
} % 9
\day{}{\vspace{2.5cm}} % 10
\day{}{\vspace{2.5cm}} % 11
\day{}{\vspace{2.5cm}} % 12
\day{}{\vspace{2.5cm}} % 13
\day{}{\vspace{2.5cm}} % 14
\day{}{\vspace{2.5cm}} % 15
\day{}{\vspace{2.5cm}} % 16
\day{}{\vspace{2.5cm}} % 17
\day{}{\vspace{2.5cm}} % 18
\day{}{\vspace{2.5cm}} % 19
\day{}{\vspace{2.5cm}} % 20
\day{}{\vspace{2.5cm}} % 21
\day{}{\vspace{2.5cm}} % 22
\day{}{\vspace{2.5cm}} % 23
\day{}{\vspace{2.5cm}} % 24
\day{}{\vspace{2.5cm}} % 25
\day{}{\vspace{2.5cm}} % 26
\day{}{\vspace{2.5cm}} % 27
\day{}{\vspace{2.5cm}} % 28
\day{}{\vspace{2.5cm}} % 29
\day{}{\vspace{2.5cm}} % 30
\day{}{\vspace{2.5cm}} % 31
% Un-comment the \BlankDay below if the bottom line of the calendar is missing
%\BlankDay
% Un-comment to start counting again after 31
%\setcounter{calendardate}{1}
%\day{}{\vspace{2.5cm}} % 1
%\day{}{\vspace{2.5cm}} % 2
%\day{}{\vspace{2.5cm}} % 3
%----------------------------------------------------------------------------------------
\finishCalendar
\end{calendar}
\newpage
\section{day1 - 01.01.2017}\label{01012017}
\begin{itemize}
\item did some setup things
\item $\dots$
\end{itemize}
\subsection{hardware}\label{01012017_h}
My tedious written composition of annoying hardware setup.
\newpage
~
\newpage
\subsection{software}\label{01012017_s}
The fun part: software!
\newpage
\section{day2 - 02.01.2017}\label{02012017}
\section{first test project}\label{02012017_a}
some text
\newpage
\section{video shoot}\label{02012017_b}
notes on my video log recording
\newpage
~
\newpage
\section{something important}\label{09012017}
\end{document}
(我不想删除太多信息,这样就不会有人误以为这不是来自latextemplates.com。此外,我不知道实际上最低要求是什么)
编辑2:
第一个外包问题: 将‘\today’烘焙到源文件(或自定义辅助文件)中
编辑1:
我仍在调查此问题,因此...... 有用的信息,例如有关 ToC 的生成、该项目所需的更深层次的 TeX(编程)或任何其他信息,也将非常感激!
答案1
我认为这也许能实现你想要的。
在发送我的代码之前,请注意以下事项要求需要满足:
- 编译器必须是LuaLaTeX。
- 下载MD5工具并将其放在源文件夹中。这是为了为章节/子章节创建有效的标签。
- 放入
mycode.lua
源文件夹。
mycode.lua
-- for debugging
-- https://github.com/kikito/inspect.lua
-- inspect = require("inspect")
-- https://github.com/kikito/md5.lua/blob/master/md5.lua
md5 = require("md5")
function strip(str)
return string.gsub(str, "^%s*(.-)%s*$", "%1")
end
function split_strip(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
local stripped = strip(str)
table.insert(t, stripped)
end
return t
end
function parse_time(time_str)
local tab = split_strip(time_str, "%-")
assert(#tab == 2, "invalid section format")
local day_str = string.gsub(tab[1], "day%s+", "")
local day_ind = tonumber(day_str)
assert(day_ind ~= nil, "invalid day index")
-- day/month/year format
local tab_mdy = split_strip(tab[2], "%.")
assert(#tab_mdy == 3, "invalid date format")
local month = tab_mdy[2]
local day = tab_mdy[1]
local year = tab_mdy[3]
local tp = os.time{year=year, month=month, day=day, hour=0, sec=1}
local date = os.date("*t", tp)
return {
day_ind=day_ind,
date=date
}
end
function label_section(storage, section_str)
return md5.sumhexa(section_str)
end
function label_subsection(storage, subsection_str)
local offset = storage["offset"]
local subsec_size = 0
if storage["subsection"][offset] ~= nil then
subsec_size = #storage["subsection"][offset]
end
local full_str = storage["section"][offset][1] .. subsection_str .. tostring(subsec_size)
return md5.sumhexa(full_str)
end
-- no integrity check for section headings
-- avoid duplicated dates
-- must have day 1
function register_section(storage, date_str)
local parse_res = parse_time(date_str)
local offset = parse_res["day_ind"]
if offset == 1 then
storage["num_blanks"] = parse_res["date"]["wday"] - 1
end
storage["offset"] = offset
local label = label_section(storage, date_str)
local t_in = {date_str, label}
storage["section"][offset] = t_in
storage["subsection"][offset] = {}
return t_in
end
function register_subsection(storage, subsection_str)
local offset = storage["offset"]
local label = label_subsection(storage, subsection_str)
local t_in = {subsection_str, label}
table.insert(storage["subsection"][offset], t_in)
return t_in
end
function new_storage()
local t = {}
t["section"] = {}
t["subsection"] = {}
t["num_days"] = 31
t["empty_fill"] = [[\vspace{2.5cm}]]
return t
end
function create_toc(storage)
local t = {}
table.insert(t, [[\begin{calendar}{\hsize}]])
local j = storage["num_blanks"]
for i=1,j do
table.insert(t, [[\BlankDay]])
end
table.insert(t, [[\setcounter{calendardate}{1}]])
j = storage["num_days"]
for i=1,j do
local tt = {}
if storage["section"][i] ~= nil then
local sec_label = storage["section"][i][2]
table.insert(tt, string.format([[\hyperref[%s]{\nameref{%s} \hfill \pageref{%s}}\newline]],
sec_label, sec_label, sec_label))
if storage["subsection"][i] ~= nil then
local subsec = storage["subsection"][i]
for _, val in pairs(subsec) do
local subsec_label = val[2]
table.insert(tt, string.format([[\hyperref[%s]{\nameref{%s} \hfill \pageref{%s}}\newline]],
subsec_label, subsec_label, subsec_label))
end
end
end
local str = ""
if #tt == 0 then
str = storage["empty_fill"]
else
str = table.concat(tt, "\n")
end
local day_str = string.format([[\day{}{%s}]], str)
table.insert(t, day_str)
end
table.insert(t, [[\finishCalendar]])
table.insert(t, [[\end{calendar}]])
return table.concat(t, "\n")
end
主文件
\documentclass[a4paper]{article}
\usepackage{calendar} % Use the calendar.sty style
\usepackage[colorlinks=false,linkcolor=black,bookmarks=false]{hyperref}
\usepackage[margin=0.5in]{geometry}
\usepackage{luacode}
\begin{document}
\pagestyle{empty} % Removes the page number from the bottom of the page
\noindent
\setcounter{section}{-1}
\section{Table of Contents}
\begin{center}
\textsc{\LARGE january}\\ % Month
\textsc{\large 2020} % Year
\end{center}
\directlua{
require("mycode")
% declare new global storage
storage = new_storage()
}
\let\oldsection\section
\let\oldsubsection\subsection
\renewcommand{\section}[1]{
\directlua{
cur_sec = register_section(storage, "\luaescapestring{#1}")
}
\oldsection{#1}\label{\directlua{tex.print(cur_sec[2])}}
}
\renewcommand{\subsection}[1]{
\directlua{
cur_subsec = register_subsection(storage, "\luaescapestring{#1}")
}
\oldsubsection{#1}\label{\directlua{tex.print(cur_subsec[2])}}
}
% update calendar source file
\AtEndDocument{
\directlua{
local out = create_toc(storage)
local file = io.open("\jobname.calendar", "w")
file:write(out)
file:close()
}
}
% read the calendar from file
\InputIfFileExists{\jobname.calendar}
\newpage
\section{day 1 - 01.01.2020}
\begin{itemize}
\item did some setup things
\item $\dots$
\end{itemize}
\subsection{hardware}
My tedious written composition of annoying hardware setup.
\newpage
~
\newpage
\subsection{software}
The fun part: software!
\newpage
\section{day 2 - 02.01.2020}
\section{day 5 - 05.01.2020}
some text
\newpage
\section{day 10 - 10.01.2020}
\subsection{notes on my video log recording}
\newpage
% test identical section heading
\subsection{notes on my video log recording}
\newpage
~
\newpage
\section{day 20 - 20.01.2020}
\end{document}
更多细节
核心处理完全在 Lua 中完成。我覆盖了默认\section
的\subsection
命令来实现自动标记。使用 Lua 的日期函数可以自动计算 的数量\BlankDay
。每次编译结束时,生成的日历源将存储在 中\jobname.calendar
并在下次运行中使用。
示例生成的日历源
\begin{calendar}{\hsize}
\BlankDay
\BlankDay
\BlankDay
\setcounter{calendardate}{1}
\day{}{\hyperref[457625da265eeceb803943ba0808c2aa]{\nameref{457625da265eeceb803943ba0808c2aa} \hfill \pageref{457625da265eeceb803943ba0808c2aa}}\newline
\hyperref[de221fb236d079c07b5af80cf9ecef34]{\nameref{de221fb236d079c07b5af80cf9ecef34} \hfill \pageref{de221fb236d079c07b5af80cf9ecef34}}\newline
\hyperref[17592e2d9eafe348603ebdcc29dcb4ad]{\nameref{17592e2d9eafe348603ebdcc29dcb4ad} \hfill \pageref{17592e2d9eafe348603ebdcc29dcb4ad}}\newline}
\day{}{\hyperref[96f35bbca7ba14a54c2730705f376746]{\nameref{96f35bbca7ba14a54c2730705f376746} \hfill \pageref{96f35bbca7ba14a54c2730705f376746}}\newline}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\hyperref[69cc2168ce348de7a0d82d6f597cd274]{\nameref{69cc2168ce348de7a0d82d6f597cd274} \hfill \pageref{69cc2168ce348de7a0d82d6f597cd274}}\newline}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\hyperref[a402e6830c5d12dbda9ca2fb80398cd4]{\nameref{a402e6830c5d12dbda9ca2fb80398cd4} \hfill \pageref{a402e6830c5d12dbda9ca2fb80398cd4}}\newline
\hyperref[831be6085cb90cfa125282e6b78ac8d5]{\nameref{831be6085cb90cfa125282e6b78ac8d5} \hfill \pageref{831be6085cb90cfa125282e6b78ac8d5}}\newline
\hyperref[2df3994fff21c52f583af09d414f5d2e]{\nameref{2df3994fff21c52f583af09d414f5d2e} \hfill \pageref{2df3994fff21c52f583af09d414f5d2e}}\newline}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\hyperref[4c03bdd9c0e81f64cf961a4c48199e97]{\nameref{4c03bdd9c0e81f64cf961a4c48199e97} \hfill \pageref{4c03bdd9c0e81f64cf961a4c48199e97}}\newline}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\day{}{\vspace{2.5cm}}
\finishCalendar
\end{calendar}
其他提醒
- 章节标题必须严格遵循特定格式。否则,解析机制将不起作用。尽管章节标题中有天数和日期,但只使用第 1 天的日期。因此,后续标题中的日期在某种程度上是多余的!
- 默认日期格式为 dd/mm/yyyy。