9

Based on the answer of the topic calendar with tikz I tried to translate the names of the days to German. However, it somehow doesn't align very well. How can I fix this?

Screenshot

Enter image description here

MWE

\documentclass[tikz,border=5]{standalone}
\usepackage[utf8]{inputenc}
\renewcommand\familydefault\sfdefault
\usetikzlibrary{positioning,calendar}

\colorlet{darkgreen}{green!50!black}

\newcommand{\calrow}[1]{%
\node[anchor=base east](Mon){Mo};
\node[base right=of Mon](Tue){Di};
\node[base right=of Tue](Wed){Mi};
\node[base right=of Wed](Thu){Do};
\node[base right=of Thu](Fri){Fr};
\node[base right=of Fri](Sat){Sa};
\node[base right=of Sat](Sun){So};
\node[darkgreen, above=of Thu]{\textbf{#1}};
}

\newcommand{\calperiod}[2][\currentyear]{%
  \calendar[dates=\currentyear-#2-01 to \currentyear-#2-last]
    if (Sunday) [black!50];}
\edef\currentyear{\the\year}

\begin{document}
\begin{tikzpicture}[every calendar/.style={week list},
year label/.style={
  fill=white,text=darkgreen,font=\bfseries\Large
}, current year/.store in=\currentyear,
current year=2017]
\matrix[%
row 1/.style={darkgreen,node distance=.3ex},%
row 3/.style={darkgreen,node distance=.3ex},
row 5/.style={darkgreen,node distance=.3ex},
row 7/.style={darkgreen,node distance=.3ex},
column sep=1ex,%
draw=darkgreen,thick,rounded corners=5pt
]{%

% first row: week day and month
\calrow{Januar} & \calrow{Februar} & \calrow{März} \\
\calperiod{01} & \calperiod{02} & \calperiod{03} \\[1ex]

% second row: calendar
\calrow{April} & \calrow{Mai} & \calrow{Juni} \\
\calperiod{04} & \calperiod{05} & \calperiod{06} \\[1ex]

% third row: week day and month
\calrow{Juli} & \calrow{August} & \calrow{September} \\
\calperiod{07} & \calperiod{08} & \calperiod{09} \\[1ex]

% forth row: calendar
\calrow{Oktober} & \calrow{November} & \calrow{Dezember} \\
\calperiod{10} & \calperiod{11} & \calperiod{12} \\[1ex]\\
};

\end{tikzpicture}
\end{document}

My problem

I don't know how to modify

\newcommand{\calrow}[1]{%
    \node[anchor=base east](Mon){Mo};
    \node[base right=of Mon](Tue){Di};
    \node[base right=of Tue](Wed){Mi};
    \node[base right=of Wed](Thu){Do};
    \node[base right=of Thu](Fri){Fr};
    \node[base right=of Fri](Sat){Sa};
    \node[base right=of Sat](Sun){So};
    \node[darkgreen, above=of Thu]{\textbf{#1}};
    }

to get things to align perfectly.

1
  • 2
    You can influence the horizontal distance between days by adding day xshift=19pt or another length fo your choice to \begin{tikzpicture} (see chapter 45, page 525 of the pgf manual)
    – leandriis
    Dec 31, 2017 at 12:35

3 Answers 3

9

The spacing between days can be controlled using the day xshift option of \calendar. Note that this spacing is measured between the east anchors of consecutive days.

\calendar[dates=\currentyear-#2-01 to \currentyear-#2-last, day xshift=1.8em]

We can then set the space between east anchors of consecutive day labels to be the same:

\newcommand{\calrow}[1]{\node[anchor=east] (Mon){Mo};
    \node[right=1.8em of Mon.east, anchor=east](Tue){Di};
    ...
}

Edit: If you want the day labels to left-align with the day numbers, one solution is to use a phantom node that has the width of a day number (i.e., 24), and then place the day labels based on their west anchor:

\newcommand{\calrow}[1]{
    % phantom node
    \node[anchor=east, draw=none] (start) {\phantom{24}};
    % left-aligned nodes
    \node[anchor=west] at (start.west) (Mon){Mo};
    \node[right=1.8em of Mon.west, anchor=west](Tue){Di};
    ...
}

calendar

Full code with left-aligned day labels:

\documentclass[tikz,border=5]{standalone}
\renewcommand\familydefault\sfdefault
\usetikzlibrary{positioning,calendar}

\colorlet{darkgreen}{green!50!black}
\colorlet{holiday}{black!50}
\newcommand{\calrow}[1]{
    % phantom node
    \node[anchor=east, draw=none] (start) {\phantom{24}};
    % left-aligned nodes
    \node[anchor=west] at (start.west) (Mon){Mo};
    \node[right=1.8em of Mon.west, anchor=west](Tue){Di}; \node[right=1.8em of Tue.west, anchor=west](Wed){Mi};
    \node[right=1.8em of Wed.west, anchor=west](Thu){Do}; \node[right=1.8em of Thu.west, anchor=west](Fri){Fr};
    \node[right=1.8em of Fri.west, anchor=west](Sat){Sa}; \node[right=1.8em of Sat.west, anchor=west](Sun){So};
    \node[darkgreen, above=of Thu]{\textbf{#1}};
}

\newcommand{\calperiod}[2][\currentyear]{%
    \calendar[dates=\currentyear-#2-01 to \currentyear-#2-last, day xshift=1.8em]
    if (Sunday) [holiday] \holidays;}
\edef\currentyear{\the\year}
\newcommand{\holidays}{% holidays in Italy
    if (equals=01-01) [holiday]%
    if (equals=01-06) [holiday]%
    if (equals=04-04) [holiday]%
    if (equals=04-05) [holiday]%
    if (equals=04-25) [holiday]%
    if (equals=05-01) [holiday]%
    if (equals=05-01) [holiday]%
    if (equals=06-02) [holiday]%
    if (equals=08-15) [holiday]%
    if (equals=11-01) [holiday]%
    if (equals=12-08) [holiday]%
    if (equals=12-25) [holiday]%
    if (equals=12-26) [holiday]%
}
\begin{document}
    \begin{tikzpicture}[every calendar/.style={week list},
    year label/.style={
        fill=white,text=darkgreen,font=\bfseries\Large
    }, current year/.store in=\currentyear,
    current year=2017]
    \matrix[%
    row 1/.style={darkgreen,node distance=.3ex},%
    row 3/.style={darkgreen,node distance=.3ex},
    row 5/.style={darkgreen,node distance=.3ex},
    row 7/.style={darkgreen,node distance=.3ex},
    column sep=1ex,%
    draw=darkgreen,thick,rounded corners=5pt,%
    append after command={ 
        \pgfextra{\edef\matrixname{\tikzlastnode}}
        node [year label/.try, right=1ex of \matrixname.south west] {\currentyear}
        node [year label/.try, right=1ex of \matrixname.north west] {\currentyear}
        node [year label/.try, left=1ex of \matrixname.south east] {\currentyear}
        node [year label/.try, left=1ex of \matrixname.north east] {\currentyear}
    }
    ]{%

        % first row: week day and month
        \calrow{Januar} & \calrow{Februar} & \calrow{M\"arz} \\
        \calperiod{01} & \calperiod{02} & \calperiod{03} \\[1ex]

        % second row: calendar
        \calrow{April} & \calrow{Mai} & \calrow{Juni} \\
        \calperiod{04} & \calperiod{05} & \calperiod{06} \\[1ex]

        % third row: week day and month
        \calrow{Juli} & \calrow{August} & \calrow{September} \\
        \calperiod{07} & \calperiod{08} & \calperiod{09} \\[1ex]

        % forth row: calendar
        \calrow{Oktober} & \calrow{November} & \calrow{Dezember} \\
        \calperiod{10} & \calperiod{11} & \calperiod{12} \\[1ex]\\
    };

    \end{tikzpicture}
\end{document}
5
  • Your solution seems to right align with the numbers below. How would you modify your solution to left align with the numbers below? Dec 31, 2017 at 12:47
  • 1
    @Sr.Schneider you can use a phantom node for that. See my edited answer.
    – wimi
    Dec 31, 2017 at 14:02
  • Now there's only the centered version missing ;-) Dec 31, 2017 at 15:56
  • 1
    @Sr.Schneider Just replace all instances of west inside the definition of \calrow with center ;)
    – wimi
    Dec 31, 2017 at 16:20
  • Is the phantom node necessary for the centered version? Dec 31, 2017 at 16:25
8

I wanted to see if I could make the process a bit smarter and avoid having to manually tinker with too many lengths. I take one weekday ("Mo") as a reference, measure its width, and then use that to set various things in the code. Additionally, there is a parameter \padding which can be adjusted as needed. It can now cope with three-letter weekdays as well (whether or not that is a good idea in the first place shall be left as an exercise to the reader -- it can start looking a bit cramped, though again, \padding).

2-letter weekdays

3-letter weekdays

Some slight manual fiddling was still required by setting every day/.append style={inner xsep=0.2ex}, though now that it's set up, it doesn't seem to require changing anymore (at least not in the few different configurations I've tried out). Maybe somebody can a nice way of automating that too.

\documentclass[tikz,border=5]{standalone}
\usepackage[utf8]{inputenc}
\renewcommand\familydefault\sfdefault
\usetikzlibrary{positioning,calendar}

\colorlet{darkgreen}{green!50!black}

% Adjust formatting here
\newcommand*\refday{Mo}
\newlength\padding
\setlength\padding{0.25ex} % Set as needed

\newcommand{\calrow}[1]{%
    \begin{scope}[every node/.style={
        anchor=east,
        align=right,% or left, depending on preference
        inner sep=0,
        outer sep=\padding,
        minimum width={width("\refday")},
        text width={width("\refday")}
    }
]
        \node[anchor=base east](Mon){Mo};
        \node[base right=0mm of Mon](Tue){Di};
        \node[base right=0mm of Tue](Wed){Mi};
        \node[base right=0mm of Wed](Thu){Do};
        \node[base right=0mm of Thu](Fri){Fr};
        \node[base right=0mm of Fri](Sat){Sa};
        \node[base right=0mm of Sat](Sun){So};
    \end{scope}%
    \node[darkgreen, above=of Thu]{\textbf{#1}};
}

\newcommand{\calperiod}[2][\currentyear]{%
  \calendar[dates=\currentyear-#2-01 to \currentyear-#2-last]
    if (Sunday) [black!50];}
\edef\currentyear{\the\year}

\begin{document}
\begin{tikzpicture}[
    day xshift={width("\refday") + 2 * \padding},% Double the outer sep of the \calrow scope
    every day/.append style={inner xsep=0.2ex},
    every calendar/.style={week list},
    year label/.style={
        fill=white,text=darkgreen,font=\bfseries\Large
    },
    current year/.store in=\currentyear,
    current year=2017]
\matrix[%
row 1/.style={darkgreen,node distance=.3ex},%
row 3/.style={darkgreen,node distance=.3ex},
row 5/.style={darkgreen,node distance=.3ex},
row 7/.style={darkgreen,node distance=.3ex},
column sep=1ex,%
draw=darkgreen,thick,rounded corners=5pt
]{%

% first row: week day and month
\calrow{Januar} & \calrow{Februar} & \calrow{März} \\
\calperiod{01} & \calperiod{02} & \calperiod{03} \\[1ex]

% second row: calendar
\calrow{April} & \calrow{Mai} & \calrow{Juni} \\
\calperiod{04} & \calperiod{05} & \calperiod{06} \\[1ex]

% third row: week day and month
\calrow{Juli} & \calrow{August} & \calrow{September} \\
\calperiod{07} & \calperiod{08} & \calperiod{09} \\[1ex]

% forth row: calendar
\calrow{Oktober} & \calrow{November} & \calrow{Dezember} \\
\calperiod{10} & \calperiod{11} & \calperiod{12} \\[1ex]\\
};

\end{tikzpicture}
\end{document}
2

With matrix

If you use a style like weekday above defined as

\tikzset{
  every weekday/.style={anchor=south east, node contents=\%w.},
  weekday above/.style={
    if = {(day of month=1) [days={append after command={
          node [at={(\tikzlastnode.north east)},
                alias=@firstweekday, every weekday]}}]},
    if = {(day of month=2, day of month=3, day of month=4,
           day of month=5, day of month=6, day of month=7) [
          days={append after command={
          node [at={(@firstweekday.south east-|\tikzlastnode.south east)},
                every weekday]}}]}}}

you can let calendar do the work on placing the weekdays with

every calendar/.append style = weekday above

There's also a style called month label above centered but its horizontal placement isn't very precise.

However, we can use Thurday's node to place the month's node above it by defining:

\tikzset{
  weekday above and month label centered above weekdays/.style={
    weekday above, every weekday/.append style={
      name=\pgfcalendarprefix-weekday-\pgfcalendarcurrentweekday},
    if = {(day of month=8) {\tikzmonthcode}},
    every month/.style={
      at=(\pgfcalendarprefix-weekday-3.north), anchor=south}}}

Code

\documentclass[ngerman,multi=tikzpicture,varwidth=false,border=5]{standalone}
\usepackage{babel,translator,tikz}
\usepackage[utf8]{inputenc}
\renewcommand\familydefault\sfdefault
\usetikzlibrary{calendar}
\colorlet{darkgreen}{green!50!black}
\newcommand{\calperiod}[2][\pgfkeysvalueof{/tikz/current year}]{%
  \calendar[dates=#1-#2-01 to #1-#2-last] if (Sunday) [black!50];}
\tikzset{
  current year/.initial=2017,
  every weekday/.style={anchor=south east, node contents=\%w.},
  weekday above/.style={
    if = {(day of month=1) [days={append after command={
      node [at={(\tikzlastnode.north east)}, alias=@firstweekday, every weekday]}}]},
    if = {(day of month=2, day of month=3, day of month=4, day of month=5, day of month=6,
           day of month=7) [ days={append after command={
      node[at={(@firstweekday.south east-|\tikzlastnode.south east)}, every weekday]}}]}},
  weekday above and month label centered above weekdays/.style={
    weekday above, every weekday/.append style={
      name=\pgfcalendarprefix-weekday-\pgfcalendarcurrentweekday},
    if = {(day of month=8) {\tikzmonthcode}},
    every month/.style={at=(\pgfcalendarprefix-weekday-3.north), anchor=south}}}
\begin{document}
\tikz[every calendar/.style={
    week list, weekday above and month label centered above weekdays,
    every month/.append style={darkgreen, anchor=south, font=\bfseries}},
  day xshift=4ex]
\matrix[row sep=3ex, column sep=2ex,
  draw=darkgreen, thick, rounded corners=5pt,
  every weekday/.append style=darkgreen,
]{
\calperiod{01} & \calperiod{02} & \calperiod{03} \\
\calperiod{04} & \calperiod{05} & \calperiod{06} \\
\calperiod{07} & \calperiod{08} & \calperiod{09} \\
\calperiod{10} & \calperiod{11} & \calperiod{12} \\
};
\end{document}

Without matrix

But we can also let calendar do everything without a matrix with a style like week list three columns defined as

\tikzset{
  current week in month/.initial=0,
  week list three columns/.style={
    execute before day scope=%
      \ifdate{day of month=1}{\ifdate{equals=\pgfcalendarbeginiso}{}{%
        \pgftransformyshift
          {\pgfkeysvalueof{/tikz/current week in month}*(\tikz@lib@cal@yshift)}%
        \pgfkeyssetvalue{/tikz/current week in month}{0}
        \pgftransformxshift
          {(mod(\numexpr\%m0-1\relax,3) ? 1 : -2) * (\tikz@lib@cal@month@xshift)}%
        \pgftransformyshift{!mod(\numexpr\%m0-1\relax,3)*
          -(\tikz@lib@cal@month@yshift)}}}{},
    execute at begin day scope=%
      \pgftransformxshift{\pgfcalendarcurrentweekday*(\tikz@lib@cal@xshift)},
    execute after day scope=%
      \ifdate{Sunday}{%
        \pgftransformyshift{-(\tikz@lib@cal@yshift)}%
        \pgfmathadd@{\pgfkeysvalueof{/tikz/current week in month}}{1}%
        \pgfkeyslet{/tikz/current week in month}\pgfmathresult}{},
    tikz@lib@cal@width=7, month xshift=32ex, month yshift=26ex}}

The backgrounds library is used to draw a rectangle around the whole picture.

Code

\documentclass[ngerman,multi=tikzpicture,varwidth=false,border=5,convert]{standalone}
\usepackage{babel,translator,tikz}
\usepackage[utf8]{inputenc}
\renewcommand\familydefault\sfdefault
\usetikzlibrary{calendar,backgrounds}
\colorlet{darkgreen}{green!50!black}
\makeatletter
\tikzset{
  week list three columns/.style={
    execute before day scope=%
      \ifdate{day of month=1}{\ifdate{equals=\pgfcalendarbeginiso}{}{%
        \pgftransformyshift
          {\pgfkeysvalueof{/tikz/current week in month}*(\tikz@lib@cal@yshift)}%
        \pgfkeyssetvalue{/tikz/current week in month}{0}
        \pgftransformxshift
          {(mod(\numexpr\%m0-1\relax,3) ? 1 : -2) * (\tikz@lib@cal@month@xshift)}%
        \pgftransformyshift{!mod(\numexpr\%m0-1\relax,3)*
          -(\tikz@lib@cal@month@yshift)}}}{},
    execute at begin day scope=%
      \pgftransformxshift{\pgfcalendarcurrentweekday*(\tikz@lib@cal@xshift)},
    execute after day scope=%
      \ifdate{Sunday}{%
        \pgftransformyshift{-(\tikz@lib@cal@yshift)}%
        \pgfmathadd@{\pgfkeysvalueof{/tikz/current week in month}}{1}%
        \pgfkeyslet{/tikz/current week in month}\pgfmathresult}{},
    tikz@lib@cal@width=7, month xshift=32ex, month yshift=26ex}}
\makeatother
\tikzset{
  current year/.initial=2017, current week in month/.initial=0,
  every weekday/.style={anchor=south east, node contents=\%w.},
  weekday above/.style={
    if = {(day of month=1) [days={append after command={
      node [at={(\tikzlastnode.north east)}, alias=@firstweekday, every weekday]}}]},
    if = {(day of month=2, day of month=3, day of month=4, day of month=5, day of month=6,
           day of month=7) [ days={append after command={
      node[at={(@firstweekday.south east-|\tikzlastnode.south east)}, every weekday]}}]}},
  weekday above and month label centered above weekdays/.style={
    weekday above, every weekday/.append style={
      name=\pgfcalendarprefix-weekday-\pgfcalendarcurrentweekday},
    if = {(day of month=8) {\tikzmonthcode}},
    every month/.style={at=(\pgfcalendarprefix-weekday-3.north), anchor=south}}}
\begin{document}
\tikz[background rectangle/.style={draw=darkgreen, thick, rounded corners=5pt},
      show background rectangle]
  \calendar [
    week list three columns,
    weekday above and month label centered above weekdays,
    every month/.append style={darkgreen, font=\bfseries},
    every weekday/.append style=darkgreen,
    day xshift=4ex, dates=2017-01-01 to 2017-12-31];
\end{document}

Output

enter image description here

You must log in to answer this question.

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