0

I am trying to make an academic calendar package. I know there's a veritable horde of them out there and I've looked through many of them but can't seem to find one I like. Since I am (usually) pretty competent with LaTeX, I figured I would just write my own. This has led to a mildly annoying issue that should be an easy fix but I can't seem to get there.

The Relevant Commands:

Right now I have the calendar being generated fine minus a small issue when trying to populate a day on the calendar. I will include all of the .sty file at the bottom, but it's quite long and pretty hacky still (still writing the rough draft), so I will extract the important parts here;

The calendar is (purposely) pre-populated, with each day having it's own command for it's contents, then I defined a "DayContent" command to renew the relevent day's command to populate the desired content (ideally I'd actually populate the calendar by building a conditional that checks if a command has been defined for that day, and if not, it would populate it with the default blank dated tile). The command is as follows:

\newcommand{\dayContent}[3]{% This puts content on the given day of the calendar.
                        % Syntax: \dayContent{Month}{Day}{Agenda}
                        %       Renews the content of the command for day "Day" and month "Month" to display "Agenda"
\expandafter\def\csname calendarDay#2Month#1\endcsname{%
    {\raggedleft #2}% We want dates in the top right corner
    \scriptsize #3% Calendars are small, write inside it small.
    \refstepcounter{dayVal}
}%

}

After I have generated the necessary commands and determined what months are going to be displayed (all in the termCalendar.sty code below) I use a printCalendar command to then print everything;

\newcommand{\printCalendar}{% The user command to make the calendar
\monthLabel
\setcounter{monthMax}{\arabic{MonthOne}}% Start with the first Month
\stepcounter{curMonthNum}% Step counter to get the right number of days
\begin{center}
{\large \bfseries \curMonthText}% Print month's name at head
\end{center}
\begin{tabularx}{\textwidth}{|l|X|X|X|X|X|r|}% Print Month's Calendar Heading
\hline \textbf{Sun}& \textbf{Monday}& \textbf{Tuesday}& \textbf{Wednesday}& \textbf{Thursday}& \textbf{Friday}& \textbf{Sat}\\ \hline\hline
\genFirstWeek% This will generate the first week, we need to be careful about semesters where the month ends in the same week the semester starts.
\genLastWeek\\ \hline% In this case we only need a first and last week Otherwise there would be as many \genFullWeek commands between the genFirst and genLast as we need.
\end{tabularx}
\stepcounter{monthVal}

\genFirstWeek, \genLastWeek commands are basically just wrappers to check some conditionals and populate the weeks as needed depending on what day of the week the first day of the month and last day of the month land on.

The Problem:

Everything populates great, except that if I want to have multiple lines on a given calendar day, they get indented and I can't seem to eliminate the indent no matter what I do.

(Following Text in [ ] is no longer accurate, See Edit Below; No longer using a nested tabular environment as the line break doesn't break tabularx) [I need to contain the linebreak in something so that the line break doesn't percolate out into the tabularx environment and generate errors. Currently I'm doing this by trying to wrap the contents in a tabular environment (thus nesting a tabular environment inside the tabularx environment) which ... kind of works.]

eg, I use the following code for the first day of classes:

\dayContent{8}{22}{
First Day Of Class\\
\textbf{Intro \& Syllabus}}

The problem is, it has a small indent that I can't get rid of no matter what. (No longer relevant text in [ ]) [You might see the @{} above, which was a solution I tried to pull from here, but it doesn't seem to work (and I don't understand it at all... does it need a \makeatletter? Because the answer suggests no, but it doesn't work either way). Regardless, the nested tabular environment was because] I couldn't figure out how to get minipage working in any way that made sense... ideally I'd love each calendar day cell to be it's own minipage, but I have no idea how to do that.

(\raggedleft issue resolved by David Carlisle, see comments below) Also, a much less important but somewhat irksome point; the \raggedleft command above doesn't seem to get the calendar's date number in the top right as I thought it would. Any suggestions on how to fix that? And any idea why it doesn't work?

To be clear, I could probably hack at this and get something that works, but I am actually more curious as to why this indent is there, even after things like \noindent and even \hspace{(negative length)} are added. I don't really know how indentation works in LaTeX at a basic/primative level other than that it saves some "length" values somewhere and uses those somehow... but the mechanics elude me. If there are any references to read about this, I would really appreciate links to them in addition to any answers.

Thanks!

EDIT As was pointed out by David Carlisle in the comments below, it turns out that tabularx makes p-columns and thus supports line breaks. Fortunately this means I can ditch the nested tabular environment. Unfortunately I still get the same exact behavior, ie the small indent at the start of each line inside the calendar day's cell.

As promised; the full sty code:

%
% Tex Type: Latex2e
\NeedsTeXFormat{LaTeX2e}
% Provides Package:
\ProvidesClass{termCalendar}

% Required Packages for this style file
\RequirePackage{tabularx}
\RequirePackage{ifthenx}
\RequirePackage{forloop}
\RequirePackage[margin=1in]{geometry}

% New Counters
\newcounter{term}% Easier to track terms by a numeric value and have a detection for people sucking at writing terms.
\setcounter{term}{0}
\newcounter{countMonth}
\newcounter{MonthOne}
\newcounter{MonthTwo}
\newcounter{MonthThree}
\newcounter{MonthFour}
\newcounter{MonthFive}
\setcounter{MonthFive}{0}
\newcounter{Iteration1}% Generic Iteration Variable
\newcounter{dayVal}% Counter for the day's date.
\setcounter{dayVal}{1}
\newcounter{monthVal}% Current month number
\newcounter{monthMax}% Current month's total days
\newcounter{firstDay}% First day of the month's location. 1=Sunday, 2=Monday, etc.
\setcounter{firstDay}{1}
\newcounter{curMonthNum}% Cuurrent Month number.
\newcounter{firstOfMonth}
\newcounter{endWeek}
\newcommand{\curMonthText}{}
\newcounter{tmp}

%New Commands:
\newcommand{\term}[1]{
    \ifthenelse{\equal{#1}{Fall}}{\setcounter{term}{1}}% Set "Fall" to value 1
    {
    \ifthenelse{\equal{#1}{fall}}{\setcounter{term}{1}}% Set "fall" to value 1
        {
        \ifthenelse{\equal{#1}{Spring}}{\setcounter{term}{2}}% Set "Spring" to value 2
        {
        \ifthenelse{\equal{#1}{spring}}{\setcounter{term}{2}}% Set "Spring" to value 2
            {
            \ifthenelse{\equal{#1}{SummerC}}{\setcounter{term}{3}}% Set "SummerC" to value 3
            {
            \ifthenelse{\equal{#1}{summerC}}{\setcounter{term}{3}}% Set "summerC" to value 3
            {
            \ifthenelse{\equal{#1}{Summerc}}{\setcounter{term}{3}}% Set "Summerc" to value 3
            {
            \ifthenelse{\equal{#1}{summerc}}{\setcounter{term}{3}}% Set "summerc" to value 3
            {
                \ifthenelse{\equal{#1}{SummerA}}{\setcounter{term}{4}}% Set "SummerA" to value 4
                {
                \ifthenelse{\equal{#1}{summerA}}{\setcounter{term}{4}}% Set "summerA" to value 4
                {
                \ifthenelse{\equal{#1}{Summera}}{\setcounter{term}{4}}% Set "Summera" to value 4
                {
                \ifthenelse{\equal{#1}{summera}}{\setcounter{term}{4}}% Set "summera" to value 4
                    {
                    \ifthenelse{\equal{#1}{SummerB}}{\setcounter{term}{5}}% Set "SummerB" to value 5
                    {
                    \ifthenelse{\equal{#1}{summerB}}{\setcounter{term}{5}}% Set "summerB" to value 5
                    {
                    \ifthenelse{\equal{#1}{Summerb}}{\setcounter{term}{5}}% Set "Summerb" to value 5
                    {
                    \ifthenelse{\equal{#1}{summerb}}{\setcounter{term}{5}}% Set "summerb" to value 5
                    {
                    \PackageError{calendar}{Unrecognized Term #1! Please enter "Fall", "Spring", "SummerC", "SummerA", or "SummerB"}
                    }
                    }
                    }
                    }
                    }
                }
                }
                }
                }
            }
            }
            }
            }
        }
        }
    }


\genDays% Populate Calendar numbers
}


\newcommand{\genDays}{
\ifthenelse{\expandafter\equal{\arabic{term}}{1}}{% If the term is 1, then it's Fall, ie August through December
    \setcounter{countMonth}{5}% Term spans 5 months in total
    \setcounter{curMonthNum}{8}% Term starts on month 8
    \setcounter{MonthOne}{31}% First month aka August has 31 days
    \genDayCommands{8}{31}
    \setcounter{MonthTwo}{30}% Second Month, aka September
    \genDayCommands{9}{30}
    \setcounter{MonthThree}{31}% Third Month, aka October
    \genDayCommands{10}{31}
    \setcounter{MonthFour}{30}% Fourth Month, aka November
    \genDayCommands{11}{30}
    \setcounter{MonthFive}{31}% Fifth Month, aka December
    \genDayCommands{12}{31}
    }% End of Fall term options
    {
    \ifthenelse{\expandafter\equal{\arabic{term}}{2}}{% Else, if it's Spring
        \setcounter{countMonth}{5}
        \setcounter{curMonthNum}{1}% Term starts on month 1
        \setcounter{MonthOne}{31}% First month aka January has 31 days
        \genDayCommands{1}{31}
        \setcounter{MonthTwo}{28}% Second Month, aka February
        \genDayCommands{2}{28}
        \setcounter{MonthThree}{31}% Third Month, aka March
        \genDayCommands{3}{31}
        \setcounter{MonthFour}{30}% Fourth Month, aka April
        \genDayCommands{4}{30}
        \setcounter{MonthFive}{31}% Fifth Month, aka May
        \genDayCommands{5}{31}
        }% End of Spring Term options
        {
        \ifthenelse{\expandafter\equal{\arabic{term}}{3}}{% Else, if it's SummerC
            \setcounter{countMonth}{4}
            \setcounter{curMonthNum}{5}% Term starts on month 5
            \setcounter{MonthOne}{31}% First month aka May has 31 days
            \genDayCommands{5}{31}
            \setcounter{MonthTwo}{30}% Second Month, aka June
            \genDayCommands{6}{30}
            \setcounter{MonthThree}{31}% Third Month, aka July
            \genDayCommands{7}{31}
            \setcounter{MonthFour}{31}% Fourth Month, aka August
            \genDayCommands{8}{31}
            }% End of Spring Term options
        {}% Else... not yet implemented.
        }
    }
}





\newcommand{\genFirstWeek}{% Generate the first week of the month
\ifthenelse{\arabic{dayVal}>\arabic{firstOfMonth}}{% First check if we are starting at the start of the month, or somewhere partway in.
    %If we are starting partway through the month, it's the first month, in which case we can populate tiles all week until we hit the end of the month.
    \setcounter{endWeek}{\arabic{dayVal}}
    \addtocounter{endWeek}{6}% Add 6 to offset the < sign below.
    \ifthenelse{\arabic{endWeek}<31}{% If there is room for a full week of dates then do so
        \genFullWeek
        }
        {% If there is not enough room, then we are on the last week of the month, so we can use lastweek
        \genLastWeek
        }
}% End of the case where we are starting partway through the month. Now we may assume we are starting at the beginning.
{% Begin of case where we are starting at the first of the month.
    \forloop{Iteration1}{2}{\arabic{Iteration1} < 8}{
    \ifthenelse{\arabic{firstOfMonth}<\arabic{Iteration1}}{% Now, check to make sure we are populating the month, starting on the correct day of the week. If so, use a dated tile.
        \csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &
        }
        {% If this isn't the correct day of the week to start, then don't use a dated tile.
        \formCell{} &
        }
        }
    % We still need to add one last dated tile to the list.
    \csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname
}% End of case where we are starting at the first of the month.
}

\newcommand{\genLastWeek}{%Generate the last week of the month
\setcounter{firstOfMonth}{1}
    \stepcounter{monthMax}% Step the "last day" counter because forloops and ifthenelse don't like <= or >=
\forloop{Iteration1}{1}{\arabic{Iteration1} < 7}{% Use forloop to place a dated tile or a blank tile as necessary
    \ifthenelse{\arabic{dayVal}<\arabic{monthMax}}{%
        \stepcounter{firstOfMonth}
%        \arabic{firstOfMonth}
%        \arabic{monthMax}
%        \arabic{dayVal}
        \csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &}
        {
        \formCell{}&
        \stepcounter{dayVal}
        }
    }
    \addtocounter{monthMax}{-1}% Undo the step counter from above because we want to check equality now, not inequality
    \ifthenelse{\arabic{dayVal}=\arabic{monthMax}}{% If it is equal, use another dated tile.
       % \setcounter{firstOfMonth}{1}% This also means the last dated tile is a Saturday, so reset firstofMonth too.
        \csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname}
        {% Otherwise, use a blank tile.
        \formCell{}
        }
\setcounter{dayVal}{1}% Reset to finding the first day of the week.
}

%%%% Commands to populate the calendar
\newcommand{\genFullWeek}{% Generate a week in the calendar; Requires pre-set dayVal and monthVal counters.
\csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &
\csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &
\csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &
\csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &
\csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &
\csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname &
\csname calendarDay\arabic{dayVal}Month\arabic{monthVal}\endcsname \\
\hline
}





\newcommand{\formCell}[1]{
    {\raggedleft #1}% We want dates in the top right corner
}

\newcommand{\genDayCommands}[2]{% This command will generate 7 days for the month of #1.
\setcounter{monthMax}{#2}
\stepcounter{monthMax}
\forloop{Iteration1}{1}{\arabic{Iteration1} < \arabic{monthMax}}{
    \expandafter\def\csname calendarDay\arabic{Iteration1}Month#1\endcsname{
        \expandafter\formCell{\arabic{dayVal}}
        \refstepcounter{dayVal}
        }
    }
}

\newcommand{\dayContent}[3]{% This puts content on the given day of the calendar.
                            % Syntax: \dayContent{Month}{Day}{Agenda}
                            %       Renews the content of the command for day "Day" and month "Month" to display "Agenda"
    \expandafter\def\csname calendarDay#2Month#1\endcsname{%
        {\raggedleft #2}% We want dates in the top right corner

        \scriptsize #3
        \refstepcounter{dayVal}
    }%
}


\newcommand{\monthLabel}{% Used to generate the month label at the top of the given month's calendar.
\setcounter{tmp}{\arabic{curMonthNum}}
\expandafter\ifthenelse\expandafter{\value{tmp}=1}{\renewcommand{\curMonthText}{January}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=2}{\renewcommand{\curMonthText}{February}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=3}{\renewcommand{\curMonthText}{March}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=4}{\renewcommand{\curMonthText}{April}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=5}{\renewcommand{\curMonthText}{May}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=6}{\renewcommand{\curMonthText}{June}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=7}{\renewcommand{\curMonthText}{July}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=8}{\renewcommand{\curMonthText}{August}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=9}{\renewcommand{\curMonthText}{September}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=10}{\renewcommand{\curMonthText}{October}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=11}{\renewcommand{\curMonthText}{November}}{%
\expandafter\ifthenelse\expandafter{\value{tmp}=12}{\renewcommand{\curMonthText}{December}}{%
}}}}}}}}}}}}% Closing all the above ifthenelse statements
}

\newcommand{\printCalendar}{% The user command to make the calendar
\monthLabel
\setcounter{monthMax}{\arabic{MonthOne}}
\stepcounter{curMonthNum}
\begin{center}
{\large \bfseries \curMonthText}
\end{center}
\begin{tabularx}{\textwidth}{|l|X|X|X|X|X|r|}
\hline \textbf{Sun}& \textbf{Monday}& \textbf{Tuesday}& \textbf{Wednesday}& \textbf{Thursday}& \textbf{Friday}& \textbf{Sat}\\ \hline\hline
\genFirstWeek% This will generate the first week, we need to be careful about semesters where the month ends in the same week the semester starts.
\genLastWeek\\ \hline
\end{tabularx}
\stepcounter{monthVal}

\monthLabel
\setcounter{monthMax}{\arabic{MonthTwo}}
\stepcounter{curMonthNum}
\begin{center}
{\large \bfseries \curMonthText}
\end{center}
\begin{tabularx}{\textwidth}{|l|X|X|X|X|X|r|}
\hline \textbf{Sun}& \textbf{Monday}& \textbf{Tuesday}& \textbf{Wednesday}& \textbf{Thursday}& \textbf{Friday}& \textbf{Sat}\\ \hline\hline
\genFirstWeek\\ \hline% This will generate the first week, we need to be careful about semesters where the month ends in the same week the semester starts.
\genFullWeek
\genFullWeek
\genFullWeek
\genFullWeek
\genLastWeek\\ \hline
\end{tabularx}
\stepcounter{monthVal}

\monthLabel
\setcounter{monthMax}{\arabic{MonthThree}}
\stepcounter{curMonthNum}
\begin{center}
{\large \bfseries \curMonthText}
\end{center}
\begin{tabularx}{\textwidth}{|l|X|X|X|X|X|r|}
\hline \textbf{Sun}& \textbf{Monday}& \textbf{Tuesday}& \textbf{Wednesday}& \textbf{Thursday}& \textbf{Friday}& \textbf{Sat}\\ \hline\hline
\genFirstWeek\\ \hline% This will generate the first week, we need to be careful about semesters where the month ends in the same week the semester starts.
\genFullWeek
\genFullWeek
\genFullWeek
\genLastWeek\\ \hline
\end{tabularx}
\stepcounter{monthVal}

\monthLabel
\setcounter{monthMax}{\arabic{MonthFour}}
\stepcounter{curMonthNum}
\begin{center}
{\large \bfseries \curMonthText}
\end{center}
\begin{tabularx}{\textwidth}{|l|X|X|X|X|X|r|}
\hline \textbf{Sun}& \textbf{Monday}& \textbf{Tuesday}& \textbf{Wednesday}& \textbf{Thursday}& \textbf{Friday}& \textbf{Sat}\\ \hline\hline
\genFirstWeek\\ \hline% This will generate the first week, we need to be careful about semesters where the month ends in the same week the semester starts.
\genFullWeek
\genFullWeek
\genFullWeek
\genLastWeek\\ \hline
\end{tabularx}
\stepcounter{monthVal}

\monthLabel
\setcounter{monthMax}{\arabic{MonthFour}}
\stepcounter{curMonthNum}
\begin{center}
{\large \bfseries \curMonthText}
\end{center}
\begin{tabularx}{\textwidth}{|l|X|X|X|X|X|r|}
\hline \textbf{Sun}& \textbf{Monday}& \textbf{Tuesday}& \textbf{Wednesday}& \textbf{Thursday}& \textbf{Friday}& \textbf{Sat}\\ \hline\hline
\genFirstWeek\\ \hline% This will generate the first week, we need to be careful about semesters where the month ends in the same week the semester starts.
\genFullWeek
\genFullWeek
\end{tabularx}
\stepcounter{monthVal}
}
  • the \raggedleft does nothing as you close the group (so undoing its effect) before the end of the paragraph, you could put \par before the } also I don't see why you need a nested tabular as you are using X columns which are p columns so allow linebreaks and multiple paragraphs – David Carlisle Aug 19 '18 at 19:45
  • you are also introducing lots of unwanted white space all the { \if need to have % after the { – David Carlisle Aug 19 '18 at 19:47
  • Well don't I feel foolish, I didn't realize the p-columns supported line breaks... hah, thanks! As I said, I don't understand how indent works, do you mind explaining your comment on the \raggedleft? And I usually go back and clean up the things like { \if stuff (like I said, it's still on a rough pass, but for some reason it didn't occur to me that it is generating white space everywhere... I'll go through and take a look for that, thanks! – Jason Aug 19 '18 at 19:53
  • tex does linebreaking as an optimisation over a whole paragraph, not line-by-line, so it is the settings at the end of the paragraph that are used, so {\raggedright zzzzz} does nothing as the } ends the group and all the raggedright settings are discarded before the paragraph is set. – David Carlisle Aug 19 '18 at 19:56
  • Ahhhh that makes so much sense. I always assumed that it used the settings at the start of the paragraph, not the end, and I always had endless problems trying to get stuff to type-set exactly how I wanted. Man that explains so much past frustration heh. Thanks! – Jason Aug 19 '18 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.