My wife is crafting together a calendar full of family photos, and she asked me to print off some simple 2016 calendar grids that she found online. I looked at the templates and vomited. They had URLs on them.
Because I suffer from developerism, I asked my wife if she’d let me try and generate some calendar pages for her. She agreed and even sat and watched me code up the script that I share below. She is rarely the direct benefactor of my career and skill set, and we will forever treasure this night when she watched me code up a calendar generator. Especially when we run it each year for the rest of our lives.
A page from the end results looks some like this:
The generator is written in Ruby and its output is LaTeX source, which can be fed into a LaTeX interpreter like pdflatex.
#!/usr/bin/env ruby
# ----------------------------------------------------------------------------
# FILE: caltex
# AUTHOR: Chris Johnson
# DATE: Dec 22 2015
#
# Generates the LaTeX source for a calendar for a given month. Written with
# my wife watching.
#
# Usage: ./caltex year [start-month [end-month]]
#
# To generate a PDF for all the months of 2016, run the following:
#
# ./caltex 2016 > 2016.tex
# pdflatex 2016.tex
# ----------------------------------------------------------------------------
require 'date'
def emit_month_page year, month
starts_on = Date.new(year, month, 1)
ends_on = Date.new(year, month, -1)
# We need enough boxes for all the days of the month plus whatever number of
# leading empty boxes in the first week before the first day. These will be
# chunked into rows of 7.
nweeks = ((ends_on.day + starts_on.wday) / 7.0).ceil
# Echo the month name, centered and bold and huge.
puts '\begin{center}\Huge\bf'
puts starts_on.strftime('%B')
puts '\end{center}'
# Now for the grid.
puts '\begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|} \hline'
# Center the weekdays in their cells.
weekdays = %w{Sunday Monday Tuesday Wednesday Thursday Friday Saturday}
print weekdays.map { |weekday| "\\centering\\arraybackslash{}#{weekday}" }.join('&')
puts '\\\\ \hline'
# Walk the grid. Translate the (week, weekday) 2D coordinate into a logical
# day of month. Only the ones in [1, 28|29|30|31] get a label. The rule is to
# push the label down a bit.
height = nweeks == 6 ? 58 : 75
(0...nweeks).each do |week|
labels = (0..6).map do |weekday|
day_of_month = week * 7 + weekday - starts_on.wday + 1
if day_of_month >= 1 && day_of_month <= ends_on.day
"\\rule{0pt}{3.5ex}\\large#{day_of_month}"
else
""
end
end
print labels.join(' & ')
puts " \\\\[#{height}pt] \\hline"
end
puts '\end{tabularx}\clearpage'
end
case ARGV.size
when 1
year = ARGV[0].to_i
start_month = 1
end_month = 12
when 2
year = ARGV[0].to_i
start_month = ARGV[1].to_i
end_month = start_month
when 3
year = ARGV[0].to_i
start_month = ARGV[1].to_i
end_month = ARGV[2].to_i
else
STDERR.puts "Usage: #{$0} year [start-month [end-month]]"
exit 1
end
puts <<'EOF'
\documentclass[landscape]{article}
\usepackage[margin=0.7in]{geometry}
\setlength{\pdfpagewidth}{\paperwidth}
\setlength{\pdfpageheight}{\paperheight}
\usepackage{tabularx}
\pagestyle{empty}
\renewcommand{\familydefault}{\sfdefault}
\begin{document}
EOF
(start_month..end_month).each do |month|
emit_month_page(year, month)
end
puts <<'EOF'
\end{document}
EOF
Happy new year!
Leave a Reply