TransWikia.com

Calendar instead of table of contents

TeX - LaTeX Asked by BadAtLaTeXProgramming on September 29, 2021

Situation

I am planning to take some notes in a ‘laboratory book’-ish latex document (compare these templates 1,2). The protocol will span a large time and entries might vary from a few items to several sides.

In order to keep better track of what I did when, I would like to have a calendar (e.g. this or this) that shows section titles (and maybe the page number). These should link to the sections (like done with pictures here).

Obviously one can use hyperref and simply do it by hand. However an TOC in such manner that is generated automatically was preferable.

Question

How can I create a TOC that looks like a calendar?

(or: a calendar that acts like a TOC)

(probably not Minimal) Working Example

Neither looking particularly nice nor quick to create (even if I used a custom command for the entries):

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 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}

defCalendarVersion{3.1}
defCalendarVersionDate{2009/04/24}

ProvidesClass{calendar}[CalendarVersionDate
LaTeX2e class file `calendar' vCalendarVersion]

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}
newcountStartingDayNumber

defdayheader#1#2{
    noindent
    begin{minipage}[t]{0.87hsize}
        noindent
        raggedright
        textit{#1}
    end{minipage}
    begin{minipage}[t]{0.1hsize}
        noindent
        raggedleft
        textit{#2}
    end{minipage}
}
defactivities#1{
    parbox{hsize}{vspace*{5pt}raggedrightscriptsize #1}smallskip
}

let@colbreak=&

defprintdayname#1{hfiltextsc{#1}hfil}

newcommand{dayname}[1]{
    ifnum#1=1 Sundayelse
    ifnum#1=2 Mondayelse
    ifnum#1=3 Tuesdayelse
    ifnum#1=4 Wednesdayelse
    ifnum#1=5 Thursdayelse
    ifnum#1=6 Fridayelse
    ifnum#1=7 Saturdayelse
    PackageError{calendar}{Unrecognized day number: #1!}
    fififififififi
}

StartingDayNumber=1
newenvironment{calendar}[1]{
    newdimen@calendarwidth
    @calendarwidth=#1
    begingroup
    def@calendarmode{@CALtrue}
    defday##1##2{
        if@calendarmode@CALtrueelsePackageWarning{calendar}{The 'day' macro is expected to be used in the 'calendar' environment!}fi
        ifnum@currentdaynum>7global@currentdaynum=1fi
        globaladvance@currentdaynum by 1
        dayheader{##1}{thecalendardate}defdaysep{vskip1pthrulevskip1pt}
        activities{##2}
        addtocounter{calendardate}{1}
        ifnum@currentdaynum>7@arraycrhlineelse&fi
    }
    deffinishCalendar{
        ifnum@currentdaynum=6 &hlineelse
        ifnum@currentdaynum=5 &&hlineelse
        ifnum@currentdaynum=4 &&&hlineelse
        ifnum@currentdaynum=3 &&&&hlineelse
        ifnum@currentdaynum=2 &&&&&hlineelse
        ifnum@currentdaynum=1 &&&&&&hline
        fifififififi
    }
    defBlankDay{
        if@calendarmode@CALtrueelsePackageWarning{calendar}{The 'calendarday' macro is expected to be used in the 'calendar' environment!}fi
        ifnum@currentdaynum>7global@currentdaynum=1fi
        globaladvance@currentdaynum by 1
        addtocounter{calendardate}{1}
        ifnum@currentdaynum>7@arraycrhlineelse&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=1fiprintdayname{dayname{@currday}} globaladvance@currday by 1 &
    ifnum@currday>@numdays@currday=1fiprintdayname{dayname{@currday}} globaladvance@currday by 1 &
    ifnum@currday>@numdays@currday=1fiprintdayname{dayname{@currday}} globaladvance@currday by 1 &
    ifnum@currday>@numdays@currday=1fiprintdayname{dayname{@currday}} globaladvance@currday by 1 &
    ifnum@currday>@numdays@currday=1fiprintdayname{dayname{@currday}} globaladvance@currday by 1 &
    ifnum@currday>@numdays@currday=1fiprintdayname{dayname{@currday}} globaladvance@currday by 1 &
    ifnum@currday>@numdays@currday=1fiprintdayname{dayname{@currday}} globaladvance@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}

(I did not want to remove too much information so no-one would confuse this with not being from latextemplates.com. Moreover I am not aware what is actually minimally required)

produces:
enter image description here



edit2:

first outsourced question:
Bake 'today' into source file (or custom aux-file)

edit1:

I am still investigating on this issue, hence …
Helpful information, e.g. on generation of ToCs, deeper TeX(-programming) necessary for this project or anything else would be much appreciated also!

One Answer

I think this may be able to achieve what you want.

result

Before spamming my code, please be advised that following requirements need to be met:

  • The compiler must be LuaLaTeX.
  • Download md5.lua and place it in your source folder. This is for creating valid labels for sections/subsections.
  • Place mycode.lua into source folder.

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

main document

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()
}

letoldsectionsection
letoldsubsectionsubsection

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}

More details

The core processing is completely done in Lua. I overrode default section and subsection commands to achieve automatically labeling. With Lua's date function, the number of BlankDay's can be computed automatically. By the end of each compilation, the generated calendar source will be stored in jobname.calendar and used in next run.

example generated calendar source

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}

Miscellaneous reminders

  1. Section titles must strictly follow a specific format. Otherwise, the parsing mechanism will not work. Despite that fact that there are day number and date in a section title, only the date of day 1 is used. As a result, the dates in subsequent titles are somehow redundant!
  2. The date format is dd/mm/yyyy by default.

Answered by Alan Xiang on September 29, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP