Create a routine that will generate a text calendar for any year.
Test the calendar by generating a calendar for the year 1969, on a device of the time.
Choose one of the following devices:

 A line printer with a width of 132 characters.
 An IBM 3278 model 4 terminal (80×43 display with accented characters). Target formatting the months of the year to fit nicely across the 80 character width screen. Restrict number of lines in test output to 43.


(Ideally, the program will generate well-formatted calendars for any page width from 20 characters up.)

Kudos (κῦδος) for routines that also transition from Julian to Gregorian calendar.

This task is inspired by Real Programmers Don't Use PASCAL by Ed Post, Datamation, volume 29 number 7, July 1983.

THE REAL PROGRAMMER'S NATURAL HABITAT
"Taped to the wall is a line-printer Snoopy calender for the year 1969."

For further Kudos see task CALENDAR, where all code is to be in UPPERCASE.

For economy of size, do not actually include Snoopy generation in either the code or the output, instead just output a place-holder.



Related task

   Five weekends


DataGrid[
  rowHeights:{__Integer},
  colWidths:{__Integer},
  spacings:{_Integer,_Integer},
  borderWidths:{{_Integer,_Integer},{_Integer,_Integer}},
  options_Association,
  data:{__List?MatrixQ}]:=
  With[
    (*Need to make sure we have sensible defaults for the decoration options.*)
    {alignment=Lookup[options,"alignment",{0,0}],
     background=Lookup[options,"background"," "],
     dividers=Lookup[options,"dividers",{" "," "," "}],
     border=Lookup[options,"border"," "],
     dims={Length[rowHeights],Length[colWidths]}},
    (*Pad the data so that it will fit into the specified rectangle (list of lists).*)
    With[{augmentedData=PadRight[data,Times@@dims,{{{background}}}]},
      (*Create a matrix of dimensions based on desired rectangle. Once we have a matrix of cells we can "thread" these two matrices and use that data to coerce each cell into its final dimensions.*)
      With[{cellDims=ArrayReshape[Outer[List,rowHeights,colWidths],{Times@@dims,2}]},
        (*MatrixAlign, defined below, rescales and aligns each cell's data.*)
        With[{undecoratedGrid=Partition[MapThread[MatrixAlign[alignment,#1,background][#2]&, {cellDims,augmentedData}],dims[[2]]]},
          (*Add the spacing to each row.*)
          With[{dividedRows=MapThread[Transpose[Riffle[#2,{ConstantArray[dividers[[2]],{#1,spacings[[2]]}]},{2,-2,2}]]&, {rowHeights,undecoratedGrid}]},
            (*Add the spacing between rows.*)
            With[{dividedColumn=Riffle[dividedRows,{Transpose[Riffle[ConstantArray[dividers[[1]],{spacings[[1]],#}]&/@colWidths,{ConstantArray[dividers[[3]],spacings]},{2,-2,2}]]},{2,-2,2}]},
              (*Assemble all cell rows into actual character rows. We now have one large matrix.*)
              With[{dividedGrid=Catenate[Map[Flatten,dividedColumn,{2}]]},
                (*Add borders.*)
                ArrayPad[dividedGrid,borderWidths,border]]]]]]]];
 
DataGrid[dims:{_Integer,_Integer},spacings_,borderWidths_,options_,data:{__List?MatrixQ}]:=
(*Calculate the max height for each row and max width for each column, and then just call the previous DataGrid function above.*)
  With[
    {rowHeights=Flatten@BlockMap[Max[Part[#,All,All,1]]&,ArrayReshape[Dimensions/@data,Append[dims,2],1],{1,dims[[2]]}],
     colWidths=Flatten@BlockMap[Max[Part[#,All,All,2]]&,ArrayReshape[Dimensions/@data,Append[dims,2],1],{dims[[1]],1}]},
    DataGrid[rowHeights,colWidths,spacings,borderWidths,options,data]];
 
(*This could probably be simplified, but I like having all of the aligment options explicit and separate for testability.*)
MatrixAlign[{-1,-1},dims_,pad_]:=PadRight[#,dims,pad]&;
MatrixAlign[{-1,0},dims_,pad_]:=PadRight[CenterArray[#,{Dimensions[#][[1]],dims[[2]]},pad],dims,pad]&;
MatrixAlign[{-1,1},dims_,pad_]:=PadRight[PadLeft[#,{Dimensions[#][[1]],dims[[2]]},pad],dims,pad]&;
MatrixAlign[{0,-1},dims_,pad_]:=CenterArray[PadRight[#,{Dimensions[#][[1]],dims[[2]]},pad],dims,pad]&;
MatrixAlign[{0,0},dims_,pad_]:=CenterArray[#,dims,pad]&;
MatrixAlign[{0,1},dims_,pad_]:=CenterArray[PadLeft[#,{Dimensions[#][[1]],dims[[2]]},pad],dims,pad]&;
MatrixAlign[{1,-1},dims_,pad_]:=PadLeft[PadRight[#,{Dimensions[#][[1]],dims[[2]]},pad],dims,pad]&;
MatrixAlign[{1,0},dims_,pad_]:=PadLeft[CenterArray[#,{Dimensions[#][[1]],dims[[2]]},pad],dims,pad]&;
MatrixAlign[{1,1},dims_,pad_]:=PadLeft[#,dims,pad]&;
 
(*While the grid functions make no assumptions about the format of the data, we will be using them with string/character data, and we will eventually want to output a calendar as a single large string. AsString gives us a standard method for transforming a matrix of characters into a string with rows delimited by newlines.*)
AsString[matrix_List?MatrixQ]:=StringRiffle[matrix,"\n",""];