I decided not to spoil the first day of the new year by doing real work. I also wanted to get to know a bit more about lua...
The background: we produce a calendar with pictures of our kids every year. Of course, I wanted to do that with ConTeXt. There is a number of precooked scripts to produce various calendars for LaTeX, but nothing for ConTeXt, as far as I could see. So I decided to write something myself. I first did it in perl, with the Date::Calc module. Then, I tried to achieve the same result with a lua script. Without the convenience of a precooked date module, I had to do all the calculations of holidays and days of the week myself, taking my cues mostly from wikipedia, but it was relatively easy.
You invoke the script from the command line: lua calendar.lua XXXX, and it will produce a file kalenderXXXX.tex, where XXXX is a number between 1900 and 2299. This file can then be typeset with ConTeXt. The result will be a calendar for the year XXXX, with every month on one page; Sundays and German official holidays are in boldface; the first day of the week is Monday. The names of the months are in German, but that should be easy to modify. We have a simple version in black and white which we print on colored paper and into which real pictures are glued, and a digital version which we send to friends via e-mail where the pages of the typeset pdf have colored backgrounds and the pictures are included.
Neither the lua code nor the TeX code is particularly beautiful (hey, this was my first day with lua), but it may be interesting for someone out there.
#!/usr/local/bin/lua
year = arg[1]
-- create boolean switch for leap years
if (math.mod(year,4) == 0) then
if (math.mod(year,100) == 0) then
if (math.mod(year,400) == 0) then
leap_year = true
else leap_year = false end
else leap_year = true end
else leap_year = false
end
-- calculate easter sunday, according to Meeus/Jones/Butcher
-- (see http://en.wikipedia.org/wiki/Computus#Meeus.2FJones.2FButcher_Gregorian_algorithm )
a = math.mod(year,19)
b = math.floor(year / 100)
c = math.mod(year,100)
d = math.floor(b / 4)
e = math.mod(b,4)
f = math.floor((b + 8) / 25)
g = math.floor((b - f + 1) / 3)
h = math.mod((19 * a + b - d - g + 15),30)
i = math.floor(c / 4)
k = math.mod(c,4)
L = math.mod((32 + 2 * e + 2 * i - h - k),7)
m = math.floor((a + 11 * h + 22 * L) / 451)
eastersundaymonth = math.floor((h + L - 7 * m + 114) / 31)
eastersunday = math.mod((h + L - 7 * m + 114),31) + 1
-- now, calculate other Christian holidays which depend on Easter Sunday
if eastersunday + 1 <= 31 then
eastermonday,eastermondaymonth = (eastersunday + 1),eastersundaymonth
else eastermonday,eastermondaymonth = (eastersunday - 30),(eastersundaymonth + 1)
end
if eastersunday -2 >= 1 then
goodfriday,goodfridaymonth = (eastersunday - 2),eastersundaymonth
else goodfriday,goodfridaymonth = (eastersunday + 29),(eastersundaymonth - 1)
end
if leap_year == true then
if eastersundaymonth == 4 then juliansunday = 91 + eastersunday
elseif eastersundaymonth == 3 then juliansunday = 60 + eastersunday end
else if eastersundaymonth == 4 then juliansunday = 90 + eastersunday
elseif eastersundaymonth == 3 then juliansunday = 59 + eastersunday end
end
if leap_year == true then
if juliansunday + 50 <= 152 then pentecostmonday,pentecostmonth = (juliansunday - 71),5
elseif (juliansunday + 50) > 152 then pentecostmonday,pentecostmonth = (juliansunday - 102),6
end
if juliansunday + 60 <= 152 then corpusday,corpusmonth = (juliansunday - 61),5
elseif (juliansunday + 60) > 152 then corpusday,corpusmonth = (juliansunday - 92),6
end
if juliansunday + 39 <= 121 then ascensionday,ascensionmonth = (juliansunday - 52),4
elseif (juliansunday + 39) > 121 then ascensionday,ascensionmonth = (juliansunday - 82),5
end
else
if juliansunday + 50 <= 151 then pentecostmonday,pentecostmonth = (juliansunday - 70),5
elseif (juliansunday + 50) > 151 then pentecostmonday,pentecostmonth = (juliansunday - 101),6
end
if juliansunday + 60 <= 151 then corpusday,corpusmonth = (juliansunday - 60),5
elseif (juliansunday + 60) > 151 then corpusday,corpusmonth = (juliansunday - 91),6
end
if juliansunday + 39 <= 120 then ascensionday,ascensionmonth = (juliansunday - 51),4
elseif (juliansunday + 39) > 120 then ascensionday,ascensionmonth = (juliansunday - 81),5
end
end
-- we open our outfile
outfile = io.open("kalender"..year..".tex","w")
outfile:write("%%% calendar for the year "..year..", produced by the lua script\n%%% calendar.lua on "..os.date().."\n\n")
-- and first write the header of our document
outfile:write("\\enableregime[utf]\n\n\\setuppagenumbering[state=stop]\n\n\\setuplayout[footer=0cm,\n\t margin=0cm,\n\t backspace=1cm,\n\t
width=fit]\n\n\\setupcolors[state=start]\n\n\\definecolor [mycyan] [r=0, g=0.8, b=1]\n\n\\starttext\n\n%\\showframe\n\n% \\setupbackgrounds[page]
[background=color,backgroundcolor=darkred]\n\n% \\midaligned{\\externalfigure[title][width=.9\\textwidth]}\n\n\\switchtobodyfont[38pt]\n\n
\\startalignment[middle]\n\n\\strut\n\n\\vfill\n\n"..year.."\n\n\\blank[2cm]\n\nKalender\n\n\\blank[2cm]\n\n\\strut\n\n\\stopalignment\n\n
\\page\n\n\\switchtobodyfont[20pt]\n\n\\strut\n\n\\vfill\n\n% \\midaligned{\\externalfigure[january][width=.9\\textwidth]}\n\n
% \\setupbackgrounds[page] [background=color,backgroundcolor=orange]\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Januar}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
-- calculate day of week for 1/1, according to Babwani's formula
-- (see http://www.geocities.com/sohaelbabwani/algorithm.html )
myyear = math.abs(year)
if myyear < 2000 then n,y = 19,(myyear - 1900)
else n,y = 20,(myyear - 2000)
end
if leap_year == true then
janfirst = math.mod((math.floor((5 * y) / 4) + 6 + 1 - (2 * (math.mod(n,4)))),7)
else
janfirst = math.mod((math.floor((5 * y) / 4) + 0 + 1 - (2 * (math.mod(n,4)))),7)
end
if janfirst == 0 then janfirst = 7 end
if janfirst == 1 then janfirst = 8 end
janindent = janfirst - 1
for jan = 1,janindent do outfile:write("\\NC ") end
if janfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("{\\bf 1} ") end
for day = 2,31 do
if leap_year == true then
weekday = math.mod((math.floor((5 * y) / 4) + 6 + day - (2 * (math.mod(n,4)))),7)
else
weekday = math.mod((math.floor((5 * y) / 4) + 0 + day - (2 * (math.mod(n,4)))),7)
end
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
elseif day == 6 then outfile:write("\\NC {\\bf "..day.."} ")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[february][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n
\\midaligned{\\sc Februar}\n\n\\placetable[bottom][none]{none}{\n
\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
if leap_year == true then
febult,febvar = 29,2
else febult,febvar = 28,3
end
febfirst = math.mod((math.floor((5 * y) / 4) + febvar + 1 - (2 * (math.mod(n,4)))),7)
if febfirst == 0 then febfirst = 7 end
if febfirst == 1 then febfirst = 8 end
febindent = febfirst - 1
for feb = 1,febindent do outfile:write("\\NC ") end
if febfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,febult do
weekday = math.mod((math.floor((5 * y) / 4) + febvar + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[march][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc März}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
marfirst = math.mod((math.floor((5 * y) / 4) + 3 + 1 - (2 * (math.mod(n,4)))),7)
if marfirst == 0 then marfirst = 7 end
if marfirst == 1 then marfirst = 8 end
marindent = marfirst - 1
for mar = 1,marindent do outfile:write("\\NC ") end
if marfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,31 do
weekday = math.mod((math.floor((5 * y) / 4) + 3 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
elseif goodfridaymonth == 3 and day == goodfriday then outfile:write("\\NC {\\bf "..day.."} ")
elseif eastermondaymonth == 3 and day == eastermonday then outfile:write("\\NC {\\bf "..day.."} ")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[april][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc April}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
aprfirst = math.mod((math.floor((5 * y) / 4) + 6 + 1 - (2 * (math.mod(n,4)))),7)
if aprfirst == 0 then aprfirst = 7 end
if aprfirst == 1 then aprfirst = 8 end
aprindent = aprfirst - 1
for apr = 1,aprindent do outfile:write("\\NC ") end
if aprfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,30 do
weekday = math.mod((math.floor((5 * y) / 4) + 6 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
elseif goodfridaymonth == 4 and day == goodfriday then outfile:write("\\NC {\\bf "..day.."} ")
elseif eastermondaymonth == 4 and day == eastermonday then outfile:write("\\NC {\\bf "..day.."} ")
elseif ascensionmonth == 4 and day == ascensionday then outfile:write("\\NC {\\bf "..day.."} ")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[may][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Mai}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
mayfirst = math.mod((math.floor((5 * y) / 4) + 1 + 1 - (2 * (math.mod(n,4)))),7)
if mayfirst == 0 then mayfirst = 7 end
if mayfirst == 1 then mayfirst = 8 end
mayindent = mayfirst - 1
for may = 1,mayindent do outfile:write("\\NC ") end
if mayfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("{\\bf 1} ") end
for day = 2,31 do
weekday = math.mod((math.floor((5 * y) / 4) + 1 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
elseif ascensionmonth == 5 and day == ascensionday then outfile:write("\\NC {\\bf "..day.."} ")
elseif corpusmonth == 5 and day == corpusday then outfile:write("\\NC {\\bf "..day.."} ")
elseif pentecostmonth == 5 and day == pentecostmonday then outfile:write("\\NC {\\bf "..day.."} ")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[june][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Juni}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
junfirst = math.mod((math.floor((5 * y) / 4) + 4 + 1 - (2 * (math.mod(n,4)))),7)
if junfirst == 0 then junfirst = 7 end
if junfirst == 1 then junfirst = 8 end
junindent = junfirst - 1
for jun = 1,junindent do outfile:write("\\NC ") end
if junfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,30 do
weekday = math.mod((math.floor((5 * y) / 4) + 4 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
elseif ascensionmonth == 6 and day == ascensionday then outfile:write("\\NC {\\bf "..day.."} ")
elseif corpusmonth == 6 and day == corpusday then outfile:write("\\NC {\\bf "..day.."} ")
elseif pentecostmonth == 6 and day == pentecostday then outfile:write("\\NC {\\bf "..day.."} ")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[july][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Juli}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
julfirst = math.mod((math.floor((5 * y) / 4) + 6 + 1 - (2 * (math.mod(n,4)))),7)
if julfirst == 0 then julfirst = 7 end
if julfirst == 1 then julfirst = 8 end
julindent = julfirst - 1
for jul = 1,julindent do outfile:write("\\NC ") end
if julfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,31 do
weekday = math.mod((math.floor((5 * y) / 4) + 6 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[august][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc August}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
augfirst = math.mod((math.floor((5 * y) / 4) + 2 + 1 - (2 * (math.mod(n,4)))),7)
if augfirst == 0 then augfirst = 7 end
if augfirst == 1 then augfirst = 8 end
augindent = augfirst - 1
for aug = 1,augindent do outfile:write("\\NC ") end
if augfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,31 do
weekday = math.mod((math.floor((5 * y) / 4) + 2 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[september][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc September}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
sepfirst = math.mod((math.floor((5 * y) / 4) + 5 + 1 - (2 * (math.mod(n,4)))),7)
if sepfirst == 0 then sepfirst = 7 end
if sepfirst == 1 then sepfirst = 8 end
sepindent = sepfirst - 1
for sep = 1,sepindent do outfile:write("\\NC ") end
if sepfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,30 do
weekday = math.mod((math.floor((5 * y) / 4) + 5 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[october][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Oktober}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
octfirst = math.mod((math.floor((5 * y) / 4) + 0 + 1 - (2 * (math.mod(n,4)))),7)
if octfirst == 0 then octfirst = 7 end
if octfirst == 1 then octfirst = 8 end
octindent = octfirst - 1
for oct = 1,octindent do outfile:write("\\NC ") end
if octfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,31 do
weekday = math.mod((math.floor((5 * y) / 4) + 0 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
elseif day == 3 then outfile:write("\\NC {\\bf "..day.."} ")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[november][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc November}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
novfirst = math.mod((math.floor((5 * y) / 4) + 3 + 1 - (2 * (math.mod(n,4)))),7)
if novfirst == 0 then novfirst = 7 end
if novfirst == 1 then novfirst = 8 end
novindent = novfirst - 1
for nov = 1,novindent do outfile:write("\\NC ") end
if novfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("{\\bf 1} ") end
for day = 2,30 do
weekday = math.mod((math.floor((5 * y) / 4) + 3 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[december][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Dezember}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")
decfirst = math.mod((math.floor((5 * y) / 4) + 5 + 1 - (2 * (math.mod(n,4)))),7)
if decfirst == 0 then decfirst = 7 end
if decfirst == 1 then decfirst = 8 end
decindent = decfirst - 1
for dec = 1,decindent do outfile:write("\\NC ") end
if decfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end
for day = 2,31 do
weekday = math.mod((math.floor((5 * y) / 4) + 5 + day - (2 * (math.mod(n,4)))),7)
if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
elseif day == 25 then outfile:write("\\NC {\\bf "..day.."} ")
elseif day == 26 then outfile:write("\\NC {\\bf "..day.."} ")
else outfile:write("\\NC "..day.." ") end
end
outfile:write("\n\\stoptabulate\n}\n\n\\stoptext\n")
--Thomas 21:56, 1 January 2007 (CET)