About
Shop
LaTeX
Software
Books
Gallery
News
Contact
Blog
Settings
Account
Smile for the Camera: a new cybercrime short story ebook.


7.5 Displaying a Calendar

The pgfcalendar package provides the command:

\pgfcalendar{prefix}{start date}{end date}{code}

This is a loop macro that iterates from ⟨start date⟩ to ⟨end date⟩ and performs ⟨code⟩ at each iteration. Within ⟨code⟩ you can access information about the current iteration using:

In addition, within ⟨code⟩ you can also use:

Examples:

  1. To just display the day of the month from 2014-02-26 to 2014-03-15:

    produces:

    26 27 28 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15

  2. To display the date for each day from 2014-03-01 to 2014-03-04:

    {% localise effect of \let
      \let\%\pgfcalendarshorthand
      \pgfcalendar{}{2014-03-01}{2014-03-04}{\%w. \%d- \%mt \%y0\par}
    }
    

    produces:

    Sat 1 March 2014
    Sun 2 March 2014
    Mon 3 March 2014
    Tue 4 March 2014 End of Image.


The tikz package (part of the pgf bundle) provides a powerful and user-friendly way of drawing images. An in-depth discussion of the tikz package is beyond the scope of this book, but here's a very brief introduction to drawing nodes in a tikzpicture environment to help draw a simple calendar. For more detail about tikz, see the pgf user manual [102].

Within the tikzpicture environment, you can use

\path[path options] (⟨position⟩) node[node options] (⟨node name⟩) {text};

to draw a node. Alternatively you can use

\node[node options] at (⟨position⟩) (⟨node name⟩) {text};

The full syntax is more complicated, but the (⟨node name⟩) is optional, as are the key=value listspath options⟩ and ⟨node options⟩. (Spaces before and after the commas and equal signs are ignored.) The full syntax of (⟨position⟩) is also quite complicated, but here I'll just use the (⟨x⟩,⟨y⟩) syntax.

Example:

(Don't forget to load the tikz package.)

\fbox{%
 \begin{tikzpicture}
  \path (0,0) node {Mon};
  \path (1,0) node {Tue};
  \path (2,0) node {Wed};
  \path (3,0) node {Thu};
  \path (4,0) node {Fri};
  \path (5,0) node {Sat};
  \path (6,0) node {Sun};
 \end{tikzpicture}%
}

I've used the \fbox command (described in Volume 1) to put a border around the picture. Fancier borders can be created using tikz commands within the tikzpicture environment.

The above code produces:

Mon Tue Wed Thu Fri Sat Sun

It's possible to add a \pgfcalendar command to this environment and put the node drawing part in the ⟨code⟩ argument. Since \pgfcalendarcurrentweekday is an integer from 0 (Monday) to 6 (Sunday), it can be used for the ⟨x⟩ coordinate. Since tikz uses a right-handed co-ordinate system, the row below the ⟨y⟩ = 0 weekday name row displayed above needs to be negative. For example:

\fbox{%
 \begin{tikzpicture}
 % First row
  \path (0,0) node {Mon};
  \path (1,0) node {Tue};
  \path (2,0) node {Wed};
  \path (3,0) node {Thu};
  \path (4,0) node {Fri};
  \path (5,0) node {Sat};
  \path (6,0) node {Sun};
 % Second row
  \pgfcalendar{}{2014-03-01}{2014-03-02}{
   \path (\pgfcalendarcurrentweekday,-1) 
     node {\pgfcalendarcurrentday};
  }
 \end{tikzpicture}%
}

This produces:

Mon Tue Wed Thu Fri Sat Sun
01 02
End of Image.


A counter is required if more than one week needs to be displayed. For example:

% Define a new count register:
\newcount\rowcount
% Initialise:
\mycount = 1\relax
% Draw the calendar:
\fbox{%
 \begin{tikzpicture}
  % header row
  \path (0,0) node {Mon};
  \path (1,0) node {Tue};
  \path (2,0) node {Wed};
  \path (3,0) node {Thu};
  \path (4,0) node {Fri};
  \path (5,0) node {Sat};
  \path (6,0) node {Sun};
  % Now iterate through the the month of March:
  \pgfcalendar{}{2014-03-01}{2014-03-31}
  {% Draw node for current day
   \path 
     (\pgfcalendarcurrentweekday,-\rowcount) % coordinate
     node {\pgfcalendarcurrentday}; % node
   % Increment row count if today is a Sunday:
   \ifdate{Sunday}{\advance\rowcount by 1}{}
  }% end of loop
 \end{tikzpicture}%
}

This now produces the image shown in Figure 7.1.

Figure 7.1: Calendar (Days of March)
 
Mon Tue Wed Thu Fri Sat Sun
01 02
03 04 05 06 07 08 09
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
End of Image.

Nodes can have a border and background. These can be specified in the [node options]. For example:

\path (0,0) node[rectangle,draw] {Mon};

will draw a rectangular border around the node while

\path (0,0) node[circle,fill=cyan] {Mon};

will give the node a cyan circular background.

Example:

The above example can be modified to include borders and backgrounds:

% Define a new count register:
\newcount\rowcount
% Initialise:
\mycount = 1\relax
% Draw the calendar:
\fbox{%
 \begin{tikzpicture}
  \path (0,0) node[circle,fill=yellow] {Mon};
  \path (1,0) node[circle,fill=yellow] {Tue};
  \path (2,0) node[circle,fill=yellow] {Wed};
  \path (3,0) node[circle,fill=yellow] {Thu};
  \path (4,0) node[circle,fill=yellow] {Fri};
  \path (5,0) node[circle,fill=cyan] {Sat};
  \path (6,0) node[circle,fill=cyan] {Sun};
  \pgfcalendar{}{2014-03-01}{2014-03-31}{
   % Draw node for current day
   \path (\pgfcalendarcurrentweekday,-\rowcount) 
     node[rectangle,draw] {\pgfcalendarcurrentday};
   % Increment row count if today is a Sunday:
   \ifdate{Sunday}{\advance\rowcount by 1}{}
  }
 \end{tikzpicture}%
}

Instead of repeatedly using the same options it's possible to set them within a local scope using the scope environment:

% Define a new count register:
\newcount\rowcount
% Initialise:
\mycount = 1\relax
% Draw the calendar:
\fbox{%
 \begin{tikzpicture}
  \begin{scope}[every node/.style={circle,fill=yellow}]
    \path (0,0) node {Mon};
    \path (1,0) node {Tue};
    \path (2,0) node {Wed};
    \path (3,0) node {Thu};
    \path (4,0) node {Fri};
    \path (5,0) node[fill=cyan] {Sat};
    \path (6,0) node[fill=cyan] {Sun};
  \end{scope}
  \pgfcalendar{}{2014-03-01}{2014-03-31}{
   % Draw node for current day
   \path (\pgfcalendarcurrentweekday,-\rowcount) 
     node[rectangle,draw] {\pgfcalendarcurrentday};
   % Increment row count if today is a Sunday:
   \ifdate{Sunday}{\advance\rowcount by 1}{}
  }
 \end{tikzpicture}%
}

This produces the image shown in Figure 7.2.

Figure 7.2: Calendar (Node Shapes Added)
 
Result as previously except now the names for the days of the week are in filled circles and the day of month numbers are bordered by rectangles.

The nodes in the first row look a little uneven as the sizes vary according to the node contents. To neaten things up a bit, a minimum size can be imposed on the nodes:

% Define a new count register:
\newcount\rowcount
% Initialise:
\mycount = 1\relax
% Draw the calendar:
\fbox{%
 \begin{tikzpicture}
  \begin{scope}[every node/.style={circle,fill=yellow,minimum size=3em}]
    \path (0,0) node {Mon};
    \path (1,0) node {Tue};
    \path (2,0) node {Wed};
    \path (3,0) node {Thu};
    \path (4,0) node {Fri};
    \path (5,0) node[fill=cyan] {Sat};
    \path (6,0) node[fill=cyan] {Sun};
  \end{scope}
  \pgfcalendar{}{2014-03-01}{2014-03-31}{
   % Draw node for current day
   \path (\pgfcalendarcurrentweekday,-\rowcount) 
     node[rectangle,draw] {\pgfcalendarcurrentday};
   % Increment row count if today is a Sunday:
   \ifdate{Sunday}{\advance\rowcount by 1}{}
  }
 \end{tikzpicture}%
}

This produces the image shown in Figure 7.3.

Figure 7.3: Calendar (Minimum Width Set on Nodes)
 
As previous result except the circles are all the same size.

However now the nodes are bumping into each other, so they need to be moved apart. The default ⟨x⟩ and ⟨y⟩ coordinate units are 1 cm. This can be changed in the optional argument of the tikzpicture environment. For example:

% Define a new count register:
\newcount\rowcount
% Initialise:
\mycount = 1\relax
% Draw the calendar:
\fbox{%
 \begin{tikzpicture}[x=1.5cm,y=1.25cm]
  \begin{scope}[every node/.style={circle,fill=yellow,minimum size=3em}]
    \path (0,0) node {Mon};
    \path (1,0) node {Tue};
    \path (2,0) node {Wed};
    \path (3,0) node {Thu};
    \path (4,0) node {Fri};
    \path (5,0) node[fill=cyan] {Sat};
    \path (6,0) node[fill=cyan] {Sun};
  \end{scope}
  \pgfcalendar{}{2014-03-01}{2014-03-31}{
   % Draw node for current day
   \path (\pgfcalendarcurrentweekday,-\rowcount) 
     node[rectangle,draw,minimum width=1cm] {\pgfcalendarcurrentday};
   % Increment row count if today is a Sunday:
   \ifdate{Sunday}{\advance\rowcount by 1}{}
  }
 \end{tikzpicture}%
}

This produces the calendar shown in Figure 7.4.

Figure 7.4: Calendar Using Circular and Rectangular Nodes (March 2014)
 
The image shows the calender below where the weekday names are in filled circles (blue for Sat and Sun and yellow for the others). The month day numbers are all inside an unfilled rectangle with a black border.

Mon Tue Wed Thu Fri Sat Sun
01 02
03 04 05 06 07 08 09
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
End of Image.

The circle and rectangle shapes are always available, but there are other shapes as well that can be loaded via the relevant tikz library, which can be loaded in the preamble using:

\usetikzlibrary{name}

where ⟨name⟩ is the library name. For example, there are some multi-part shapes defined in the shapes.multipart library. In order to use these shapes, you not only need

\usepackage{tikz}

in the preamble but also

\usetikzlibrary{shapes.multipart}

Example:

A rectangular split node with 2 splits can be created using:

\begin{tikzpicture}
 \path (0,0)
 node[rectangle split,rectangle split parts=2,draw]
 {%
   Top
   \nodepart{two}
   Bottom
 };
\end{tikzpicture}

The \nodepart{part} command moves from the current split part to the split part identified by ⟨part⟩. In the case of a rectangular split node, the second part is identified by the keyword two. The above code produces:

Top
Bottom
with a dividing line between the "Top" and "Bottom" text.

End of Image.


With a vertical split node, such as in the above example, you can set a minimum width using the minimum width key, but you can't specify a minimum height. You can, however, specify a height for empty parts using the option rectangle split empty part height=length⟩. For example:

\begin{tikzpicture}
 \path (0,0)
 node
 [
   rectangle split,
   rectangle split parts=2,
   rectangle split empty part height=1cm,
   minimum width=2cm,
   draw
 ]
 {%
   Top
   \nodepart{two}
   % empty bottom part
 };
\end{tikzpicture}

This produces:

As before but the width is 2cm and the now empty bottom part is 1cm high.

You can specify fill colours for each part using the rectangle split part fill={colour list} option, where ⟨colour list⟩ is a comma-separated list of colours for each part, in order. For example:

\begin{tikzpicture}
 \path (0,0)
 node
 [
   rectangle split,
   rectangle split parts=2,
   rectangle split empty part height=1cm,
   rectangle split part fill={cyan,magenta},
   minimum width=2cm,
   draw
 ]
 {%
   Top
   \nodepart{two}
   % empty bottom part
 };
\end{tikzpicture}

This produces:

As before but the top part has a cyan background and the bottom part has a magenta background.

Since tikz loads the xcolor package [41], you can specify colours using the xcolor syntax. For example:

\begin{tikzpicture}
 \path (0,0)
 node
 [
   rectangle split,
   rectangle split parts=2,
   rectangle split empty part height=1cm,
   rectangle split part fill={cyan!20,magenta!5},
   minimum width=2cm,
   draw
 ]
 {%
   Top
   \nodepart{two}
   % empty bottom part
 };
\end{tikzpicture}

This produces:

As before but the colours are faded.

Now the fill colour for the top part is 20% cyan tint and the fill colour for the bottom part is 5% magenta tint. This isn't a great colour scheme, but it's just used for illustrative purposes.

Example 42. Calendar for May 2014

The above can be put together to create a calendar for the month of May 2014:

\newcount\rowcount
\rowcount=1\relax

\fbox{%
\let\%\pgfcalendarshorthand
\begin{tikzpicture}[x=1.5cm,y=1.75cm]
  \begin{scope}
    [every node/.style={rectangle,fill=green!5,minimum width=1.4cm}]
  \path (0,0) node {Mon};
  \path (1,0) node {Tue};
  \path (2,0) node {Wed};
  \path (3,0) node {Thu};
  \path (4,0) node {Fri};
  \path (5,0) node {Sat};
  \path (6,0) node {Sun};
  \end{scope}
  \pgfcalendar{}{2014-05-01}{2014-05-31}
  {
   \path (\pgfcalendarcurrentweekday,-\rowcount)
    node
    [
      rectangle split,
      minimum width=1.4cm,
      rectangle split empty part height=1cm,
      rectangle split parts=2,
      rectangle split part fill={cyan!20,magenta!4},
      draw] 
   {\%d-
    \nodepart{two}
   };
   \ifdate{Sunday}{\advance\rowcount by 1}{}
 }
\end{tikzpicture}%
}

This produces the calendar shown in Figure 7.5.

Figure 7.5: Calendar with Split Nodes (May 2014)
 
The image shows the calender below where the weekday names are inside pale green filled rectangles. The month day numbers are all inside a pale blue rectangle with a black border. Below each of these blue rectangles is a pale pink filled rectangle with a black border.

Mon Tue Wed Thu Fri Sat Sun
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
End of Image.

Suppose now I want to add some information to the calendar. For example, the two May bank holidays on the 5th and 26th of May. Additionally, suppose I also want a different colour background for weekends and bank holidays, for example, a light grey. The bank holiday information can be stored in control sequences whose names are in the format prefix⟩-⟨YYYY⟩-⟨MM⟩-⟨DD, which is the format used by \pgfcalendarsuggestedname. Recall etoolbox's \csdef command described in §2.1.1 Macro Definitions. This can be used to define these control sequences:

\csdef{cal-2014-05-05}{Early May BH}
\csdef{cal-2014-05-26}{Spring BH}

The \pgfcalendar command now needs cal as the ⟨prefix⟩ so that in the ⟨code⟩ part, the current day can be checked if it's a bank holiday using:

\ifcsdef{\pgfcalendarsuggestedname}%
{%
  % Current day is a bank holiday
}%
{%
  % Current day isn't a bank holiday
}

As before a TeX register called \rowcount is defined using:

\newcount\rowcount

The actual code to generate the calendar is now:

\fbox{%
\rowcount=1\relax
\let\%\pgfcalendarshorthand
\begin{tikzpicture}[x=1.5cm,y=1.75cm]
  \begin{scope}
    [every node/.style={rectangle,fill=green!5,minimum width=1.4cm}]
  \path (0,0) node {Mon};
  \path (1,0) node {Tue};
  \path (2,0) node {Wed};
  \path (3,0) node {Thu};
  \path (4,0) node {Fri};
  \path (5,0) node {Sat};
  \path (6,0) node {Sun};
  \end{scope}
  \pgfcalendar{cal}{2014-05-01}{2014-05-31}{%
   \def\thebackground{magenta!4}%
   \ifcsdef{\pgfcalendarsuggestedname}%
   {%
     \def\thecontents{\csuse{\pgfcalendarsuggestedname}}%
     \def\thebackground{black!4}%
   }%
   {%
     \def\thecontents{\mbox{}}%
     \ifdate{weekend}{\def\thebackground{black!4}}{}%
   }%
   \path (\pgfcalendarcurrentweekday,-\rowcount)
    node
    [
      rectangle split,
      rectangle split parts=2,
      rectangle split part fill={cyan!20,\thebackground},
      draw] 
   {\%d-
    \nodepart{two}%
    \parbox[t][1cm]{1.2cm}{\small\thecontents}%
   };
   \ifdate{Sunday}{\advance\rowcount by 1}{}%
 }%
\end{tikzpicture}%
}

This produces the calendar shown in Figure 7.6.

Figure 7.6: May 2014 with Bank Holidays
 
The image shows the calender below where the weekday names are inside pale green filled rectangles. The month day numbers are all inside a pale blue rectangle with a black border. Below each of these blue rectangles is a filled rectangle with a black border. The fill colour is a pale grey for weekends and bank holidays and a pale pink for the other days.

Mon Tue Wed Thu Fri Sat Sun
1 2 3 4
5 6 7 8 9 10 11
Early May BH
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Spring BH
End of Image.

Suppose now you want to fill in the gaps at the beginning and end of the month. Recall the \foreach command mentioned in §2.7.2 Iterating Over a Comma-Separated List. This has the syntax:

\foreachvariables[options] in {list}{body}

but it's cleverer than the other list macros described in that section as you can use ... within ⟨list⟩ if the list contents can be inferred from the beginning and end of the list. For example:

\foreach \x in {1,...,10} {\x\space}

produces

1 2 3 4 5 6 7 8 9 10

Therefore, within the ⟨code⟩ part of \pgfcalendar, you can test if the current day is the first day of the month (by testing that \pgfcalendarcurrentday is equal to 1) and use \foreach to fill in the last days of the previous month:

\ifnum\pgfcalendarcurrentday=1\relax
% Fill in days from previous month if this isn't a Monday
  \ifdate{Monday}{}
  {% Get last day of previous month
    \julianday = \pgfcalendarcurrentjulian\relax
    \advance\julianday by -\pgfcalendarcurrentweekday\relax
    \foreach \x in {0,...,\numexpr\pgfcalendarcurrentweekday-1}
    {
      \pgfcalendarjuliantodate{\julianday}{\theyear}{\themonth}{\theday}
      \path (\x,-1)
       node
       [
         rectangle split,
         rectangle split parts=2,
         draw]
      {\number\theday
       \nodepart{two}
       \parbox[t][1cm]{1.2cm}{\mbox{}}%
      };
      \global\advance\julianday by 1\relax
    }
  }
\fi

This requires a new register:

\newcount\julianday

The tikz package automatically loads the pgffor package, so the \foreach command will also be available if you use tikz. Note that \foreach uses a local scope for each iteration which is why \global is required when incrementing the \julianday register. The gap at the end of the final week can also be filled with the initial days of the next month, but as with \foreach, \pgfcalendar scopes each iteration, so the row register \rowcount will need to be incremented globally so that it can be used after the loop has completed. It's also useful to store the Julian day number and the week day number for the last day of the month so they can be accessed outside the loop. This saves the need to compute them again. So the last part of ⟨code⟩ needs to replace:

\ifdate{Sunday}{\advance\rowcount by 1}{}%

with

Now the remaining days of the last row can be completed outside the \pgfcalendar loop:

 \ifnum\lastweekday < 6\relax
   \julianday = \lastjulianday\relax
   \edef\lastweekday{\number\numexpr\lastweekday+1}
   \foreach \x in {\lastweekday,...,6}
   {
     \global\advance\julianday by 1\relax
     \pgfcalendarjuliantodate{\julianday}{\theyear}{\themonth}{\theday}
     \path (\x,-\rowcount)
      node
      [
        rectangle split,
        rectangle split parts=2,
        draw]
     {\number\theday
      \nodepart{two}
      \parbox[t]{1cm}{1.2cm}{\mbox{}}%
     };
   }
 \fi

Note that you can also use \foreach to display the week day nodes:

  \foreach \x in {0,...,6}
   {\path (\x,0) node {\pgfcalendarweekdayshortname{\x}};}

The complete code is:

\newcount\rowcount
\newcount\julianday
\fbox{%
\rowcount=1\relax
\let\%\pgfcalendarshorthand
\begin{tikzpicture}[x=1.5cm,y=1.75cm]
  \begin{scope}
    [every node/.style={rectangle,fill=green!5,minimum width=1.4cm}]
    \foreach \x in {0,...,6}
     {\path (\x,0) node {\pgfcalendarweekdayshortname{\x}};}
  \end{scope}
  \pgfcalendar{cal}{2014-05-01}{2014-05-31}
  {% Is this the first day of the month?
    \ifnum\pgfcalendarcurrentday=1\relax
    % Fill in days from previous month if this isn't a Monday
    \ifdate{Monday}{}
    {% Get last day of previous month
     \julianday = \pgfcalendarcurrentjulian\relax
     \advance\julianday by -\pgfcalendarcurrentweekday\relax
     \foreach \x in {0,...,\numexpr\pgfcalendarcurrentweekday-1}
     {
      \pgfcalendarjuliantodate{\julianday}{\theyear}{\themonth}{\theday}
      \path (\x,-1)
        node
        [rectangle split,
         rectangle split parts=2,
         draw]
        {\number\theday
         \nodepart{two}
         \parbox[t][1cm]{1.2cm}{\mbox{}}%
        };
      \global\advance\julianday by 1\relax
     }
    }
    \fi
    \def\thebackground{magenta!4}%
    \ifcsdef{\pgfcalendarsuggestedname}%
    {%
      \def\thecontents{\csuse{\pgfcalendarsuggestedname}}%
      \def\thebackground{black!4}%
    }%
    {%
      \def\thecontents{\mbox{}}%
      \ifdate{weekend}{\def\thebackground{black!4}}{}%
    }%
    \path (\pgfcalendarcurrentweekday,-\rowcount)
     node
     [rectangle split,
      rectangle split parts=2,
      rectangle split part fill={cyan!20,\thebackground},
      draw] 
    {\%d-
     \nodepart{two}%
     \parbox[t][1cm]{1.2cm}{\small\thecontents}%
    };
    \ifdate{Sunday}{\global\advance\rowcount by 1}{}%
    \xdef\lastjulianday{\number\pgfcalendarcurrentjulian}
    \xdef\lastweekday{\number\pgfcalendarcurrentweekday}
  }%
  \ifnum\lastweekday < 6\relax
   \julianday = \lastjulianday\relax
   \edef\lastweekday{\number\numexpr\lastweekday+1}
   \foreach \x in {\lastweekday,...,6}
   {
     \global\advance\julianday by 1\relax
     \pgfcalendarjuliantodate{\julianday}{\theyear}{\themonth}{\theday}
     \path (\x,-\rowcount)
      node
      [rectangle split,
       rectangle split parts=2,
       draw]
     {\number\theday
      \nodepart{two}
      \parbox[t]{1cm}{1.2cm}{\mbox{}}%
     };
   }
  \fi
\end{tikzpicture}%
}

The result is shown in Figure 7.7. You can download or view a complete document.

Figure 7.7: May 2014 with Bank Holidays (including end of previous month and beginning of next month)
 
The image shows the calender below where the weekday names are inside pale green filled rectangles. The month day numbers are all inside a pale blue rectangle with a black border. Below each of these blue rectangles is a filled rectangle with a black border. The fill colour is a pale grey for weekends and bank holidays and a pale pink for the other May days. The day numbers for the previous and next months are in unfilled rectangles.

Mon Tue Wed Thu Fri Sat Sun
28 29 30 1 2 3 4
5 6 7 8 9 10 11
Early May BH
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 1
Spring BH
End of Image.

It's possible to create a general month calendar macro from the above. First a command that can be used to set information for a given date. This uses \appto so that information can be appended to a date.

\newcommand*{\addevent}[2]{%
  \ifcsdef{cal-#1}
  {% already defined so append info
    \csappto{cal-#1}{\newline #2}%
  }%
  {% not defined
    \csdef{cal-#1}{#2}%
  }
}

This has the syntax:

\addevent{date}{information}

Now the definition for the calendar month macro:

\newcommand*{\calendarmonth}[2]{%
  \fbox{%
  \rowcount=1\relax
  \let\%\pgfcalendarshorthand
  \begin{tikzpicture}[x=1.5cm,y=1.75cm]
    % display the month name at the top
    \path (3,1) node {\pgfcalendarmonthname{#2}};
    \begin{scope}
      [every node/.style={rectangle,fill=green!5,minimum width=1.4cm}]
      \foreach \x in {0,...,6}
       {\path (\x,0) node {\pgfcalendarweekdayshortname{\x}};}
    \end{scope}
    \pgfcalendar{cal}{#1-#2-01}{#1-#2-last}
    {%
      % Is this the first day of the month?
      \ifnum\pgfcalendarcurrentday=1\relax
      % Fill in days from previous month if this isn't a Monday
      \ifdate{Monday}{}
      {
        % Get last day of previous month
        \julianday = \pgfcalendarcurrentjulian\relax
        \advance\julianday by -\pgfcalendarcurrentweekday\relax
        \foreach \x in {0,...,\numexpr\pgfcalendarcurrentweekday-1}
        {
          \pgfcalendarjuliantodate
            {\julianday}{\theyear}{\themonth}{\theday}
          \path (\x,-1)
           node
           [
             rectangle split,
             rectangle split parts=2,
             draw]
          {\number\theday
           \nodepart{two}
           \parbox[t][1cm]{1.2cm}{\mbox{}}%
          };
          \global\advance\julianday by 1\relax
        }
      }
      \fi
      \def\thebackground{magenta!4}%
      \ifcsdef{\pgfcalendarsuggestedname}%
      {%
        \def\thecontents{\csuse{\pgfcalendarsuggestedname}}%
        \def\thebackground{black!4}%
      }%
      {%
        \def\thecontents{\mbox{}}%
        \ifdate{weekend}{\def\thebackground{black!4}}{}%
      }%
      \path (\pgfcalendarcurrentweekday,-\rowcount)
       node
       [
         rectangle split,
         rectangle split parts=2,
         rectangle split part fill={cyan!20,\thebackground},
         draw] 
      {\%d-
       \nodepart{two}%
       \parbox[t][1cm]{1.2cm}{\small\thecontents}%
      };
      \ifdate{Sunday}{\global\advance\rowcount by 1}{}%
      \xdef\lastjulianday{\number\pgfcalendarcurrentjulian}
      \xdef\lastweekday{\number\pgfcalendarcurrentweekday}
    }%
    \ifnum\lastweekday < 6\relax
     \julianday = \lastjulianday\relax
     \edef\lastweekday{\number\numexpr\lastweekday+1}
     \foreach \x in {\lastweekday,...,6}
     {
       \global\advance\julianday by 1\relax
       \pgfcalendarjuliantodate{\julianday}{\theyear}{\themonth}{\theday}
       \path (\x,-\rowcount)
        node
        [
          rectangle split,
          rectangle split parts=2,
          draw]
       {\number\theday
        \nodepart{two}
        \parbox[t][1cm]{1.2cm}{\mbox{}}%
       };
     }
    \fi
  \end{tikzpicture}%
  }
}

The syntax for this macro is:

\calendarmonth{YYYY}{MM}

Don't forget you also need to define the registers:

\newcount\rowcount
\newcount\julianday

Exercise 23. Calendar for 2014

Create a landscape document that has a calender month per page for 2014 (or the year of your choice). Read the tikz chapter of the pgf manual [102] to find ways of modifying the above code. You can download or view a solution.


This book is also available as A4 PDF or 12.8cm x 9.6cm PDF or paperback (ISBN 978-1-909440-07-4).

© 2015 Dickimaw Books. "Dickimaw", "Dickimaw Books" and the Dickimaw parrot logo are trademarks. The Dickimaw parrot was painted by Magdalene Pritchett.

Terms of Use Privacy Policy Cookies Site Map FAQs