12

I'm putting together a multi-month calendar in TikZ and am seeing some awkward and undesired alignment in the month labels despite using the month label above centered style. In particular, the month label appears to be centered only over the first six columns, rather than over all seven:

Observed vs Desired alignments of month label

I've reproduced my code below in a minimal context. Two questions: What's going on here, and how can I correct this alignment?

\documentclass[letterpaper]{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{calendar,shapes}

\makeatletter%
\tikzstyle{week list sunday}=[
        % Note that we cannot extend from week list,
        % the execute before day scope is cumulative
        execute before day scope={%
               \ifdate{day of month=1}{\ifdate{equals=\pgfcalendarbeginiso}{}{
               % On first of month, except when first date in calendar.
                   \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@month@yshift}%
                   \pgftransformyshift{-\pgf@y}
               }}{}%
        },
        execute at begin day scope={%
            % Because for TikZ Monday is 0 and Sunday is 6,
            % we can't directly use \pgfcalendarcurrentweekday,
            % but instead we define \c@pgf@counta (basically) as:
            % (\pgfcalendarcurrentweekday + 1) % 7
            \pgfmathsetlength\pgf@x{\tikz@lib@cal@xshift}%
            \ifnum\pgfcalendarcurrentweekday=6
                \c@pgf@counta=0
            \else
                \c@pgf@counta=\pgfcalendarcurrentweekday
                \advance\c@pgf@counta by 1
            \fi
            \pgf@x=\c@pgf@counta\pgf@x
            % Shift to the right position for the day.
            \pgftransformxshift{\pgf@x}
        },
        execute after day scope={
            % Week is done, shift to the next line.
            \ifdate{Saturday}{
                % But not if it's already the last day of the month.
                \ifdate{end of month=1}{}{
                    \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@yshift}%
                    \pgftransformyshift{-\pgf@y}
                }%
            }{}%
        },
        % This should be defined, glancing from the source code.
        tikz@lib@cal@width=7
    ]

\tikzoption{day headings}{\tikzstyle{day heading}=[#1]}
\tikzstyle{every calendar}=[week list sunday,
    month label above centered,
    month text=\textbf{\%mt~\%y-},
    day xshift = 0.5cm,
    day letter headings]
\tikzstyle{every day}=[anchor=base]
\tikzstyle{day heading}=[]
\tikzstyle{day letter headings}=[
    execute before day scope={ \ifdate{day of month=1}{%
      \pgfmathsetlength{\pgf@ya}{\tikz@lib@cal@yshift}%
      \pgfmathsetlength\pgf@xa{\tikz@lib@cal@xshift}%
      \pgftransformyshift{-\pgf@ya}
      \foreach \d/\l in {0/S,1/M,2/T,3/W,4/T,5/F,6/S} {
        \pgf@xa=\d\pgf@xa%
        \pgftransformxshift{\pgf@xa}%
        \pgftransformyshift{\pgf@ya}%
        \node[every day,day heading]{\l};%
      }
    }{}%
  }%
]

\makeatother%

% ==================================================

\begin{document}

\begin{tikzpicture}
\calendar (K) [dates=2013-01-01 to 2013-03-last]
    if (weekend) [black!40];
\draw[black](K-2013-01-21) circle (0.6em);
\draw[black](K-2013-02-13) circle (0.6em);
\draw[black](K-2013-03-10) circle (0.6em);
\draw[black](K-2013-03-31) circle (0.6em);
\end{tikzpicture}

\end{document}

Misaligned month label in TikZ calendar

2 Answers 2

7

You can increase the value for tikz@lib@cal@width for example, to 7.8:

\documentclass[letterpaper]{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{calendar,shapes}

\makeatletter%
\tikzstyle{week list sunday}=[
        % Note that we cannot extend from week list,
        % the execute before day scope is cumulative
        execute before day scope={%
               \ifdate{day of month=1}{\ifdate{equals=\pgfcalendarbeginiso}{}{
               % On first of month, except when first date in calendar.
                   \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@month@yshift}%
                   \pgftransformyshift{-\pgf@y}
               }}{}%
        },
        execute at begin day scope={%
            % Because for TikZ Monday is 0 and Sunday is 6,
            % we can't directly use \pgfcalendarcurrentweekday,
            % but instead we define \c@pgf@counta (basically) as:
            % (\pgfcalendarcurrentweekday + 1) % 7
            \pgfmathsetlength\pgf@x{\tikz@lib@cal@xshift}%
            \ifnum\pgfcalendarcurrentweekday=6
                \c@pgf@counta=0
            \else
                \c@pgf@counta=\pgfcalendarcurrentweekday
                \advance\c@pgf@counta by 1
            \fi
            \pgf@x=\c@pgf@counta\pgf@x
            % Shift to the right position for the day.
            \pgftransformxshift{\pgf@x}
        },
        execute after day scope={
            % Week is done, shift to the next line.
            \ifdate{Saturday}{
                % But not if it's already the last day of the month.
                \ifdate{end of month=1}{}{
                    \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@yshift}%
                    \pgftransformyshift{-\pgf@y}
                }%
            }{}%
        },
        % This should be defined, glancing from the source code.
        tikz@lib@cal@width=7.8
    ]

\tikzoption{day headings}{\tikzstyle{day heading}=[#1]}
\tikzstyle{every calendar}=[week list sunday,
    month label above centered,
    month text=\textbf{\%mt~\%y-},
    day xshift = 0.5cm,
    day letter headings]
\tikzstyle{every day}=[anchor=base]
\tikzstyle{day heading}=[]
\tikzstyle{day letter headings}=[
    execute before day scope={ \ifdate{day of month=1}{%
      \pgfmathsetlength{\pgf@ya}{\tikz@lib@cal@yshift}%
      \pgfmathsetlength\pgf@xa{\tikz@lib@cal@xshift}%
      \pgftransformyshift{-\pgf@ya}
      \foreach \d/\l in {0/S,1/M,2/T,3/W,4/T,5/F,6/S} {
        \pgf@xa=\d\pgf@xa%
        \pgftransformxshift{\pgf@xa}%
        \pgftransformyshift{\pgf@ya}%
        \node[every day,day heading]{\l};%
      }
    }{}%
  }%
]

\makeatother%

% ==================================================

\begin{document}

\begin{tikzpicture}
\calendar (K) [dates=2013-01-01 to 2013-03-last]
    if (weekend) [black!40];
\draw[black](K-2013-01-21) circle (0.6em);
\draw[black](K-2013-02-13) circle (0.6em);
\draw[black](K-2013-03-10) circle (0.6em);
\draw[black](K-2013-03-31) circle (0.6em);
\end{tikzpicture}

\end{document}

enter image description here

5
  • 1
    Looks pretty good, but why 7.8, specifically? Is tikz@lib@cal@width effectively a multiple of day xshift? If so, why doesn't 7 work in this example? I'm trying to understand the root cause of the problem... Commented Nov 26, 2012 at 2:53
  • After some experimentation, it seems the value which "correctly" centers the label in this example is 7.9, but I still don't have any additional insight into what that value actually represents and why 7 doesn't work as it should. Commented Nov 26, 2012 at 3:41
  • @TimParenti I really don't know; I've never used the calendar library, so I am not familiar with how it works; I found the "dirty fix" by detecting which part of your code had to do with the horizontal alignment of the month label and that's how tikz@lib@cal@width appeared. The value 7.8 was found by trial/error. Commented Nov 26, 2012 at 14:42
  • I suppose a "dirty fix" is better than no fix at all. Still leaves me with a bad taste, but hey... at least it works. I'm going to hold off on accepting this for another day or so in case anyone else can offer a more insightful solution. (But congrats on 100k!) Commented Nov 26, 2012 at 22:13
  • @TimParenti sure, I would also like to know if someone offers a "proper" calculation ("manual" guessing are by no means my favorite either). Thanks for the congratulations. Commented Nov 26, 2012 at 22:31
1

I think it's a bug in pgf. You use the month label above centered key to place the month name above the days, which is defined in the file tikzlibrarycalendar.code.tex as

\tikzset{month label above centered/.style={%
  execute before day scope={%
    \ifdate{day of month=1}{%
      {
        \pgfmathsetlength{\pgf@xa}{\tikz@lib@cal@xshift}%
        \pgf@xb=\tikz@lib@cal@width\pgf@xa%
        \advance\pgf@xb by-\pgf@xa%
        \pgf@xb=.5\pgf@xb%
        \pgftransformxshift{\pgf@xb}%
        \pgftransformxshift{-1.5ex}%
        \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@yshift}%
        \pgftransformyshift{1.25\pgf@y}
        \tikzmonthcode%
      }
    }{}},
  every month/.append style={anchor=base}
}}

Apparenty, there is an extra \pgftransformxshift{-1.5ex}, which is responsible for the misalignment. If you replace month label above centered in your code with the above definition but with \pgftransformxshift{-1.5ex} removed, you will get the correct alignment. Here is your modified MWE:

\documentclass[letterpaper]{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{calendar,shapes}

\makeatletter%
\tikzstyle{week list sunday}=[
        % Note that we cannot extend from week list,
        % the execute before day scope is cumulative
        execute before day scope={%
               \ifdate{day of month=1}{\ifdate{equals=\pgfcalendarbeginiso}{}{
               % On first of month, except when first date in calendar.
                   \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@month@yshift}%
                   \pgftransformyshift{-\pgf@y}
               }}{}%
        },
        execute at begin day scope={%
            % Because for TikZ Monday is 0 and Sunday is 6,
            % we can't directly use \pgfcalendarcurrentweekday,
            % but instead we define \c@pgf@counta (basically) as:
            % (\pgfcalendarcurrentweekday + 1) % 7
            \pgfmathsetlength\pgf@x{\tikz@lib@cal@xshift}%
            \ifnum\pgfcalendarcurrentweekday=6
                \c@pgf@counta=0
            \else
                \c@pgf@counta=\pgfcalendarcurrentweekday
                \advance\c@pgf@counta by 1
            \fi
            \pgf@x=\c@pgf@counta\pgf@x
            % Shift to the right position for the day.
            \pgftransformxshift{\pgf@x}
        },
        execute after day scope={
            % Week is done, shift to the next line.
            \ifdate{Saturday}{
                % But not if it's already the last day of the month.
                \ifdate{end of month=1}{}{
                    \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@yshift}%
                    \pgftransformyshift{-\pgf@y}
                }%
            }{}%
        },
        % This should be defined, glancing from the source code.
        tikz@lib@cal@width=7
    ]

\tikzoption{day headings}{\tikzstyle{day heading}=[#1]}
\tikzstyle{every calendar}=[week list sunday,
% Replace this:
%    month label above centered
% With this modified extract from 'tikzlibrarycalendar.code.tex':
  execute before day scope={%
    \ifdate{day of month=1}{%
      {
        \pgfmathsetlength{\pgf@xa}{\tikz@lib@cal@xshift}%
        \pgf@xb=\tikz@lib@cal@width\pgf@xa%
        \advance\pgf@xb by-\pgf@xa%
        \pgf@xb=.5\pgf@xb%
        \pgftransformxshift{\pgf@xb}%
%        \pgftransformxshift{-1.5ex}% Removed misalignment
        \pgfmathsetlength{\pgf@y}{\tikz@lib@cal@yshift}%
        \pgftransformyshift{1.25\pgf@y}
        \tikzmonthcode%
      }
    }{}},
  every month/.append style={anchor=base}
% End of replacement text. Comma follows.
    ,
    month text=\textbf{\%mt~\%y-},
    day xshift = 0.5cm,
    day letter headings]
\tikzstyle{every day}=[anchor=base]
\tikzstyle{day heading}=[]
\tikzstyle{day letter headings}=[
    execute before day scope={ \ifdate{day of month=1}{%
      \pgfmathsetlength{\pgf@ya}{\tikz@lib@cal@yshift}%
      \pgfmathsetlength\pgf@xa{\tikz@lib@cal@xshift}%
      \pgftransformyshift{-\pgf@ya}
      \foreach \d/\l in {0/S,1/M,2/T,3/W,4/T,5/F,6/S} {
        \pgf@xa=\d\pgf@xa%
        \pgftransformxshift{\pgf@xa}%
        \pgftransformyshift{\pgf@ya}%
        \node[every day,day heading]{\l};%
      }
    }{}%
  }%
]

\makeatother%

% ==================================================

\begin{document}

\begin{tikzpicture}
\calendar (K) [dates=2013-01-01 to 2013-03-last]
    if (weekend) [black!40];
\draw[black](K-2013-01-21) circle (0.6em);
\draw[black](K-2013-02-13) circle (0.6em);
\draw[black](K-2013-03-10) circle (0.6em);
\draw[black](K-2013-03-31) circle (0.6em);
\end{tikzpicture}

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .