2

I'm modifying a calendar by Robert Krause (TeXample) and trying to make the code more dynamic. I have split it up into two files. One of them contains the main code (the logic) and the other one contains all the events, vacations and so on.

My plan was to define all the events in this separate clean file and let tikz do the rest. It should loop through all the "arrays" and fill the content into the calendar.

Unfortunately I get nine error messages. All of them look like this:

Package pgf Error: No shape named cal-2016-01-01 is known.

I don't understand why this happens and I would really be pleased if anyone could help me out.

Here is the main code:

\documentclass[landscape, a4paper, ngerman, 10pt]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[ngerman]{babel}
\usepackage[T1]{fontenc}
\usepackage{tikz}                       % Use the calendar.sty style
\usepackage{translator}                 % German Month and Day names
\usepackage{fancyhdr}                   % header and footer
\usepackage{fix-cm}                     % Large year in header
\usepackage{color}                      % Colors
\usepackage[
    landscape, 
    headheight = 2cm, 
    margin=.5cm,
    top = 3.2cm, 
    nofoot
]{geometry}

\usetikzlibrary{calc}
\usetikzlibrary{calendar}
\renewcommand*\familydefault{\sfdefault}

% ------------------------------------------------------- 
%
% User defined
%
% -------------------------------------------------------

\input{termine}

% Color
\definecolor{nicegreen}{HTML}{00cc00}

% Names of Holidays are inserted by employing this macro
\def\feiertag#1#2{
    \node [
        anchor = north west, 
        text width = 3.4cm
    ] at
    ($(cal-#1.north west)+(3em, -0.2em)$) {\scriptsize{#2}};
}

% Names of Exams are inserted by employing this macro
\def\exam#1#2{
    \node [
        anchor = north west, 
        text width = 3.4cm,
        text = red,
        font = \bf
    ] at
    ($(cal-#1.north west)+(3em, -0.2em)$) {\scriptsize{#2}};
}

% Header
\renewcommand{\headrulewidth}{0.0pt}
\setlength{\headheight}{11ex}
\chead{
  \fontsize{60}{70}\selectfont\textbf{WS \year/\nextyear}
  \Large\textbf{}\hfill
}

% Footer
\cfoot{\footnotesize\texttt{}}


\begin{document}
    \pagestyle{fancy}
    \begin{center}

        % ------------------------------------------------------- 
        %
        % Wintersemester
        % September to February
        %
        % ------------------------------------------------------- 
        \begin{tikzpicture}[every day/.style={anchor = north}]
            \calendar[
                dates = \year-10-01 to \nextyear-03-31,
                name = cal,
                day yshift = 3em,
                day code = {
                    \node[
                        name = \pgfcalendarsuggestedname,
                        every day, 
                        shape = rectangle,
                        minimum height = .53cm, 
                        text width = 4.4cm, 
                        draw = gray
                    ]{\tikzdaytext};
                    \draw (-1.8cm, -.1ex) node[anchor = west]{\footnotesize%
                        \pgfcalendarweekdayshortname{\pgfcalendarcurrentweekday}
                    };
                },
                execute before day scope={
                    \ifdate{day of month = 1} {
                        % Shift right
                        \pgftransformxshift{4.8cm}
                        % Print month name 
                        \draw (0,0)node [
                            shape = rectangle, 
                            minimum height = .53cm,
                            text width = 4.4cm, 
                            fill = nicegreen, 
                            text = white, 
                            draw = nicegreen, 
                            text centered
                        ]
                        {\textbf{\pgfcalendarmonthname{\pgfcalendarcurrentmonth}\strut}};
                    }{}
                    \ifdate{workday} {
                        % normal days are white
                        \tikzset{every day/.style = {fill = white}}
                        % Vacation (Uni Bonn)
                        % ----------------------
                        % Semesterferien Sommer
                        \ifdate{between = \sommerbegin and \sommerend}{%
                            \tikzset{every day/.style = {fill = gray!30}}%
                        }{}                        
                        % Weihnachtspause
                        \ifdate{between = \weihnachtsbegin and \weihnachtsend}{%
                            \tikzset{every day/.style = {fill = gray!30}}%
                        }{}
                        % Semesterferien Winter
                        \ifdate{between = \winterbegin and \winterend}{%
                            \tikzset{every day/.style = {fill = gray!30}}%
                        }{}
                    }{}
                    % Saturdays
                    \ifdate{Saturday} {
                        \tikzset{every day/.style = {fill = nicegreen!10}}%
                    }{}
                    % Sundays
                    \ifdate{Sunday} {
                        \tikzset{every day/.style = {fill = nicegreen!20}}%
                    }{}
                },
                execute at begin day scope = {
                    % each day is shifted down according to the day of month
                    \pgftransformyshift{-.53*\pgfcalendarcurrentday cm}
                }
            ];

            % Print name of Holidays (January to June)
            \foreach \subject/\eventdate in \feiertage{
               \feiertag{\eventdate}{\subject}
            }

            % Print name of Exams (January to June)
            \foreach \subject/\eventdate in \exams{
                \exam{\eventdate}{\subject}
            }

        \end{tikzpicture}

    \end{center}
\end{document}

And here is the termine.tex:

% ------------------------------------------------------- 
% ------------------------------------------------------- 
% Define the year, holidays, exams and vacations
% ------------------------------------------------------- 
% ------------------------------------------------------- 


% ------------------------------------------------------- 
% YEAR
% ------------------------------------------------------- 

\def\year{2016}                            % Year for the calendar
\edef\nextyear{\the\numexpr\year+1\relax}  % \year + 1


% ------------------------------------------------------- 
% Holidays
% Syntax {day description}/date
% ------------------------------------------------------- 

\def\feiertage {
    {Neujahr}/\year-01-01,
    {Karfreitag}/\year-04-14,
    {Tag der dt. Einheit}/\year-10-03,
    {Reformationstag}/\year-10-31,
    {Allerheiligen}/\year-11-01,
    {1. Weihnachtstag}/\year-12-25,
    {2. Weihnachtstag}/\year-12-26,
    {Sylvester}/\year-12-31,
}


% ------------------------------------------------------- 
% EXAMS
% Syntax: {exam name}/date
% ------------------------------------------------------- 

\def\exams {
    {Quantitative Analysen}/\year-01-09,
    {Pysikalische Chemie}/\year-01-20,
    {Biochemie}/\year-02-10,
}


% ------------------------------------------------------- 
% VACATIONS
% ------------------------------------------------------- 

% Semester break (Winter)
\def\winterbegin{\nextyear-02-10}
\def\winterend{\nextyear-04-18}

% Semester break (Sommer)
\def\sommerbegin{\year-07-28}
\def\sommerend{\year-10-17}

% Christmas vacation
\def\weihnachtsbegin{\year-12-26}
\def\weihnachtsend{\nextyear-01-06}

Does anyone see an error in my code? Thank you very much in advance!

Oh and how could I fill the days of the \feiertage-"array" gray too?

Adding the following lines of code to the vacations ends up in error messages too :-(

\ifdate{workday} {
    % normal days are white
    \tikzset{every day/.style = {fill = white}}
    % Vacation (Uni Bonn)
    % ----------------------
    % Semesterferien Sommer
    \ifdate{between = \sommerbegin and \sommerend}{%
        \tikzset{every day/.style = {fill = gray!30}}%
    }{}                        
    % Weihnachtspause
    \ifdate{between = \weihnachtsbegin and \weihnachtsend}{%
        \tikzset{every day/.style = {fill = gray!30}}%
    }{}
    % Semesterferien Winter
    \ifdate{between = \winterbegin and \winterend}{%
        \tikzset{every day/.style = {fill = gray!30}}%
    }{}
    % Feiertage
    \foreach \subject/\eventdate in \feiertage{
        \ifdate{equals=\eventdate}{%
            \tikzset{every day/.style={fill=gray!30}}%
        }{}
    }
}{}

EDIT

In response to an answer by cfr I'm expanding my question here, so that it includes a comment.

Above I asked for a method to mark the events in the \feiertage-"array" gray. cfr's answer worked properly, but unfortunately it overwrote previous color settings. It was clear to me (but unfortunately I didn't ask for it), that the weekend should stay in green, or whatever color was selected. So what I was searching for, was a method, to take all the \feiertage-events and give them a gray background if they are not on a weekend. cfr was friendly enough to edit his answer, which now completely fulfills my wishes.

  • The error is from cal-#1.north west which if #1 is 2016-01-01 is accessing cal-2016-01-01 I queried that in a comment on your previous question but I wasn't sure what the intended meaning was so didn't suggest any change. – David Carlisle Dec 29 '16 at 21:54
  • @DavidCarlisle Okay, you say, that there is a problem with the cal-, which i can understand when looking at the error message. However, if I manually put a date in \exam{}{} or \feiertag{}{} it works. When doing this, the cal--prefix is still there. What should I change in your opinion? – Sam Dec 29 '16 at 22:17
  • I was hoping one of the tikz army would pass by with an answer to save me debugging exactly what goes wrong) I may trace later if no one else has answered (I don't really know a lot about tikz:-) – David Carlisle Dec 29 '16 at 22:19
  • Okay, well then let's hope for the tikz army ;-) Anyway, thanks for your help so far. I really appreciate it! – Sam Dec 29 '16 at 22:37
  • @DavidCarlisle Maybe you know an answer to my added question of the gray filling? – Sam Dec 29 '16 at 22:49
2

To answer the question you asked in your answer,

\def\feiertag#1#2{%
  \fill [gray!30, blend mode=darken] (cal-#1.north west) rectangle (cal-#1.south east);
    \node [
        anchor = north west,
        text width = 3.4cm,
    ] at ($(cal-#1.north west)+(3em, -0.2em)$) {\scriptsize{#2}};
}

EDIT

In response to the question in the comments, which is not asked at all in the original question, here's a version which applies the grey fill only if no other special colours apply. Well, actually, it is applied anyway, but then filled over by green or whatever if it is a weekend.

Note that a KOMA class does nothing here but take longer to load, so I've used article. I have altered the code to avoid overwriting existing commands (\year is already defined) and used \newcommand and friends, when possible, in line with best practice. Also, I've set the effective head height with geometry. Never set layout dimensions manually if using geometry (or similar) to manage them. The code could still use some tidying e.g. some font commands are still inside nodes' contents, but I have at least removed those which are 20+ years deprecated. (Two letter font macros should not be used in LaTeX 2e.) Etc. etc. It really could do with some streamlining, though, if only to make it easier to read.

Note that this code is slllloooooooooooooooowwwww. However, that's just PGF, I think. That is, the original code was really slow and I don't know this is noticeably slower. TikZ does not a speedy calculator make.

\documentclass[landscape, a4paper, ngerman, 10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage[T1]{fontenc}
\usepackage{tikz,xparse}
\usepackage{translator}
\usepackage{fancyhdr}
\usepackage[
  headheight = 11ex,% geometry needs to know the actual value you want! Don't change layout dimensions outside geometry, if using geometry
  margin=.5cm,
  top = 3.2cm,
  nofoot,
]{geometry}

\usetikzlibrary{calc,calendar}
\renewcommand*\familydefault{\sfdefault}

\newcommand*\myyear{2016}% don't overwrite existing commands wily-nily
\edef\nextyear{\the\numexpr\myyear+1\relax}

\newcommand*\feiertage {%
  {Neujahr}/\nextyear-01-01,%
  {Tag der dt.\ Einheit}/\myyear-10-03,%
  {Reformationstag}/\myyear-10-31,%
  {Allerheiligen}/\myyear-11-01,%
  {1. Weihnachtstag}/\myyear-12-25,%
  {2. Weihnachtstag}/\myyear-12-26,%
  {Sylvester}/\myyear-12-31%
}

\ExplSyntaxOn
\clist_new:N \g_sam_feiertage_clist
\int_new:N \l_sam_feiertag_int
\foreach \i/\j in \feiertage
{%
  \pgfcalendardatetojulian{\j}{\l_sam_feiertag_int}
  \clist_gput_right:Nx \g_sam_feiertage_clist { \int_to_arabic:n { \l_sam_feiertag_int } }
}
\cs_new_protected_nopar:Nn \sam_feiertag_test:n
{
  \int_set:Nn \l_tmpa_int {#1}
  \clist_if_in:NVT \g_sam_feiertage_clist \l_tmpa_int
  {
    \pgfcalendarmatchestrue
  }
}
\cs_generate_variant:Nn \sam_feiertag_test:n {x}
\NewDocumentCommand \testhols { m }
{
  \sam_feiertag_test:x { #1 }
}
\ExplSyntaxOff

\tikzset{
  /pgf/calendar/feiertag/.code={%
    \testhols{\pgfcalendarifdatejulian}%
  },
}

\newcommand*\exams {%
  {Quantitative Analysen}/\nextyear-01-09,%
  {Pysikalische Chemie}/\nextyear-01-20,%
  {Biochemie}/\nextyear-02-10%
}

\newcommand*\winterbegin{\nextyear-02-10}
\newcommand*\winterend{\nextyear-04-18}
\newcommand*\sommerbegin{\myyear-07-28}
\newcommand*\sommerend{\myyear-10-17}
\newcommand*\weihnachtsbegin{\myyear-12-26}
\newcommand*\weihnachtsend{\nextyear-01-06}

\definecolor{nicegreen}{HTML}{00cc00}

\newcommand*\feiertag[2]{%
  \node [ anchor = north west, text width = 3.4cm ] at ($(cal-#1.north west)+(3em, -0.2em)$) {\scriptsize{#2}};
}
\newcommand*\exam[2]{%
  \node [ anchor = north west, text width = 3.4cm, text = red, font = \bfseries ] at ($(cal-#1.north west)+(3em, -0.2em)$) {\scriptsize{#2}};
}

\renewcommand{\headrulewidth}{0pt}
\fancyhf{}
\lhead{%
  \fontsize{60}{70}\selectfont\textbf{WS \myyear/\nextyear}}

\begin{document}
\pagestyle{fancy}
\centering
\begin{tikzpicture}[every day/.style={anchor = north}]
  \calendar[
  dates = \myyear-10-01 to \nextyear-03-31,
  name = cal,
  day yshift = 3em,
  day code = {
    \node[name = \pgfcalendarsuggestedname, every day, minimum height = .53cm, text width = 4.4cm, draw = gray] {\tikzdaytext};
    \draw (-1.8cm, -.1ex) node [anchor = west, font=\footnotesize] {\pgfcalendarweekdayshortname{\pgfcalendarcurrentweekday}};
  },
  execute before day scope={
    \ifdate{day of month = 1} {
      \pgftransformxshift{4.8cm}
      \draw (0,0) node [minimum height = .53cm, text width = 4.4cm, fill = nicegreen, text = white, draw = nicegreen, text centered] {\textbf{\pgfcalendarmonthname{\pgfcalendarcurrentmonth}\strut}};
    }{}
    \ifdate{workday} {
      \tikzset{every day/.style = {fill = white}}
      \ifdate{between = \sommerbegin and \sommerend}{%
        \tikzset{every day/.style = {fill = gray!30}}%
      }{}
      \ifdate{between = \weihnachtsbegin and \weihnachtsend}{%
        \tikzset{every day/.style = {fill = gray!30}}%
      }{}
      \ifdate{between = \winterbegin and \winterend}{%
        \tikzset{every day/.style = {fill = gray!30}}%
      }{}
      \ifdate{feiertag}{%
        \tikzset{every day/.style = {fill = gray!30}}%
      }{}
    }{}
    \ifdate{Saturday} {
      \tikzset{every day/.style = {fill = nicegreen!10}}%
    }{}
    \ifdate{Sunday} {
      \tikzset{every day/.style = {fill = nicegreen!20}}%
    }{}
  },
  execute at begin day scope = {
    \pgftransformyshift{-.53*\pgfcalendarcurrentday cm}
  }
  ];
  \foreach \subject/\eventdate in \feiertage
  {
    \feiertag{\eventdate}{\subject}
  }
  \foreach \subject/\eventdate in \exams
  {
    \exam{\eventdate}{\subject}
  }
\end{tikzpicture}
\end{document}

much effort for bit of grey filling on one day ...

  • Works well, but does always apply a gray fill. What about holidays that match a saturday or sunday? Those days should always appear in their shade of green. \ifdate{Saturday}{}{\fill [gray!30, blend mode=darken] (cal-\holidaydate.north west) rectangle (cal-\holidaydate.south east);} didn't work. – Sam Dec 30 '16 at 10:15
  • @Sam That is not what you asked. You asked for them to be filled with grey. If you want more complexity, you need to set up the relevant conditionals. – cfr Dec 30 '16 at 23:44
  • Yeah, that's totally ok. I didn't think on that when I ased for it, but I noticed it when testing your code. As you see, I tried using the conditionals to check for a Saturday or a Sunday and only apply the fill if it's not one of these days, but it does not work. Maybe you know why? It seems, that tikz does not know the \isdate-command at that spot. Maybe it has to be inside the \calendar[]; but I also tried it there and it didn't work either :/ – Sam Dec 30 '16 at 23:49
  • @Sam Please see update above. But please edit your question to ask what you want to know so that the answer fits and does not just respond to a comment which may be removed at some point. – cfr Jan 2 '17 at 4:05
0

Solved!

I added comment symbols after each event and now it works. Looks as if tikz tried to draw a node to a place which did not exist...

\def\feiertage {
    {Neujahr}/\nextyear-01-01,%
    {Tag der dt. Einheit}/\year-10-03,%
    {Reformationstag}/\year-10-31,%
    {Allerheiligen}/\year-11-01,%
    {1. Weihnachtstag}/\year-12-25,%
    {2. Weihnachtstag}/\year-12-26,%
    {Sylvester}/\year-12-31%
}

Furthermore I forgot to write \nextyear to the events taking pace in the new year...

1 Question remains

How can I fill in the \feiertage-fields with gray color, like the vacations?

  • Please don't post follow-ups as answers. It is not easy to apply this answer to your MWE and get it working. Yet it is a lot to repost complete code here, as would usually be recommended, given the very maximal nature of your example. – cfr Dec 30 '16 at 3:26

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.