Back to Blog

The Month Came With a Watermark

September 18, 2026
Share:
The Month Came With a Watermark

I needed September on one sheet of paper. Empty boxes. Room to write. The first generator I opened asked me to make an account before it would draw the grid. The file I got back had someone else's mark in the corner.

I built a tab that draws the month here. Letter or A4. Sunday or Monday as the first weekday. Optional US federal holidays from a named statute. Two light lines under each day number. Download a PDF. Mixpanel still loads with Softery.io, so I will not claim the whole site never talks to a server.

The new page is Printable Calendar Generator.


The file had someone else's mark

A fridge calendar is a sheet you write on. Soccer practice. The day the rent leaves. You want the days right and the boxes empty.

What I kept hitting was a form, then a preview with a logo that was not mine. Print that and the logo is on the fridge for a month. Take it off in their editor and the page wants an account. I was not going to make a third login for a grid of boxes.

This tab starts with September 2026, Letter, weeks starting Sunday, US holidays on, the Softery.io mark off. Change a knob and the preview updates. Download writes calendar-2026-09.pdf. A range through December writes calendar-2026-09-to-2026-12.pdf. Open the file. If a day number sits on the crop line, do not print a classroom set from it.

The grid is date math. The PDF is pdf-lib 1.17.1, the library this site already uses for other paper tools. Helvetica. Rectangles. I did not raster the whole page. A printer does better with lines than with a screenshot of a webpage.

The Softery.io mark is off unless you turn it on.

That line is on the page. Classroom stacks usually want it off.


Letter is 612 by 792

Letter is 8.5 by 11 inches. At 72 points per inch that is 612 by 792. A4 is 210 by 297 mm. Both sizes come from paperSize in the layout file. The preview uses those numbers. The PDF uses the same numbers. What you see is the page that will print.

Content sits 36 points in from every edge. That is half an inch. PRINT_MARGIN_PT is 36. A lot of home printers clip about a quarter inch. If a driver still crops, pick Fit to printable area in the print dialog. That is a driver setting, not a second export from this tab.

I checked the content stream in the tests. Day cells, the title, the weekday row, and the write-in lines stay inside that inset. A translate of zero does not count as drawing outside it. Branding, when you turn it on, sits in the bottom margin, still inside 36 points.

After Download, open the file on your machine. Look at the edges. Look at the 1 and the last day of the month. If they are gone, the export is wrong.

PaperWidthHeight
Letter8.5 in, 612 pt11 in, 792 pt
A4210 mm297 mm

Empty cells stay empty

January 1, 2026 is a Thursday. dayOfWeekSunday0 returns 4 for that date. A Sunday-start grid leaves four empty cells before the 1. A Monday-start grid leaves three. Neighbour months are not filled in. The cell is blank. That is the point of a blank calendar.

I have used planners that print 30 from November in the first box of December. Fine if you wanted a planner. I wanted a month I could hang, with room to write, and not last month's leftovers in the corner.

The grid is always six weeks. Thirty-one days that start on Saturday still fit. August 1, 2026 is a Saturday. The 31 lands in the last row. Empty cells in that row stay empty too.

Weekday labels follow the start you picked. Sunday weeks read Sun through Sat. Monday weeks read Mon through Sun. The preview and the PDF share WEEKDAY_LABELS.


February 1900 has 28 days

2024 is a leap year. February has 29 days. 2000 is a leap year. 1900 is not. February 1900 has 28. That is the Gregorian century rule: a year divides by 4, except a century that does not divide by 400. The tests name those four years on purpose.

29 February 2024 is a Thursday. Same helper as January 1, 2026.

Years run from 1 to 9999. MIN_YEAR is 1. MAX_YEAR is 9999. England skipped days in 1752 when it moved onto this calendar. This page will not reconstruct that September. If you need a historical reconstruction, this is the wrong tool.

I am not going to pretend JavaScript's Date is the source of truth here. Local timezone tricks have eaten February before. The day of week is Sakamoto's method on the civil date. Days in a month are a table plus the leap rule. The tests fail if those drift.


The holiday list is a statute

The toggle is US federal holidays from 5 U.S.C. 6103. The string on the page is HOLIDAY_SOURCE. There is no holiday API. Other countries are off because a holiday depends on the country, and sometimes the county, and this page will not guess.

Thanksgiving 2026 is 26 November, the fourth Thursday. Martin Luther King Jr. Day 2026 is 19 January, the third Monday. Labor Day 2026 is 7 September, the first Monday, which is why the sample September grid can show it.

Juneteenth is on the list from 2021. 2020 does not get it. The statute added that date, and the code follows the year, not a vibe.

Turn the toggle off and November is blank days with write-in lines. Thanksgiving is gone. Observed Friday is gone. The PDF tests look for that: holidays off means the word Thanksgiving is not in the page content.

A state holiday, a school break, or a religious calendar is not in 5 U.S.C. 6103. Write those on the lines. I would rather you write them than I invent a second country's list from a blog post.


Saturday moves to Friday

Independence Day 2026 is Saturday 4 July. The observed day off is Friday 3 July. The actual date still says Independence Day. The Friday says Independence Day (observed). Both marks are in July 2026 when the toggle is on.

New Year's Day 2022 was a Saturday. The observed day is Friday 31 December 2021. The 2022 list carries that observed row with year 2021, so December 2021 can show it. The test names that object: year 2021, month 12, day 31, kind observed.

The OPM rule in the source line is: Saturday to the Friday before, Sunday to the Monday after. Fixed-date holidays use that. Thanksgiving does not move, because it is already a Thursday.

I put both the Saturday and the Friday on the grid so you can see why the office is closed on the 3rd. A blank calendar that hid the real 4th would be a lie.


Two lines under the day number

Each cell has two write-in lines. lineCount is 2. They sit inside a 6 point inset from the cell edge. Light grey, on purpose, so a pencil still reads.

The day number is in the corner. A holiday label, when the toggle is on, is a second line of smaller type. I kept the label short. "Independence Day (observed)" is already a mouthful in a cell that has to fit on A4.

I did not add a score. A calendar is not a quiz. Weather icons and moon phases are other products. This one is a month you can write on.

The title is the month name and the year. September 2026. The weekday row is under that. If you add a header photo, a 72 point strip sits between the title and the weekday row, still inside the 36 point inset. One inch tall.


A photo that does not stay

JPEG or PNG. The type is read from magic bytes, not the filename. JPEG starts FF D8 FF. PNG starts 89 50 4E 47. A file named header.jpg that is actually a PDF is refused.

Cap is 8,000,000 bytes. PHOTO_CAP_BYTES. Mixpanel sees a size bucket from sizeBucket, and jpeg or png as a kind. It does not get the photo.

The photo is not written to localStorage. It is not put on the share URL. Close the tab and it is gone. That is on purpose. A family picture on a classroom calendar is not a public card, and I will not stash it in this browser under a settings key.

The settings key is softery.printable-calendar-generator.settings. Paper, week start, holidays, the mark, and the month range. Clear settings puts you back to this month on Letter with Sunday weeks, holidays on, mark off. The photo is already gone.


Twelve months, then stop

MAX_MONTHS is 12. A range longer than that is an error. End before start is an error. Year outside 1 to 9999 is an error. Month outside 1 to 12 is an error. The copy names those, so you can fix the range without guessing.

I did not stamp a landing page per year. A thousand empty year URLs are not a calendar. One page, a range you pick, a PDF with one page per month.

Download is cancelled if you hit the button again while it is running. The abort signal is the same pattern as the other PDF tools. A twelve page file is still tiny, so there is no worker. Vector rectangles do not need one.

The status line says when the file is ready. Open the file. Check a leap February if you printed one. Check July 2026 if you wanted the observed Friday.


The share URL is knobs

Copy settings link puts paper, week start, holidays, the mark, and the month range on the query string. y, m, months, paper, week=sun|mon, holidays, brand. The page shows that URL before you copy it.

A family calendar is not a public card. The preview is there so you can see you are sending September, not the photo of the kids.

resultShared fires on copy. Mixpanel gets the channel, not the months. runStarted gets a page count bucket from resultCountBucket, plus paper, week, and holidays on or off. One page is "1". Two or three is "2-3". Twelve is "6+".

I will not put the year in analytics. The year is the user's calendar.


What this page will not do

It will not invent UK bank holidays. It will not invent a school district calendar. It will not fill in next month's numbers in this month's empty cells. It will not keep the header photo after you close the tab. It will not make a year of 12 landings under 12 URLs.

It will not claim the PDF is a signed document. For a signature on a form, use Sign a PDF. It will not stitch photos into a packet. That is Images to PDF. A bill you also need on paper belongs on Invoice Generator.

pdf-lib writes the bytes in this tab after the page has loaded. Mixpanel and Google Analytics still load with Softery.io. They see that someone opened the tool, which setting moved, and whether a download happened. They do not get the PDF.


Print one page first

  1. Open Printable Calendar Generator.
  2. The sample is September 2026. Change the year if you need this month.
  3. Pick Letter or A4. Pick Sunday or Monday.
  4. Leave holidays on if you want 5 U.S.C. 6103 on the grid. Turn them off for a blank month.
  5. Keep the Softery.io mark off unless you want it small in the footer.
  6. Hit Download PDF. Open the file. Look at the edges. Then print one copy.

If July 2026 is in the range, look at Friday 3 and Saturday 4. The Friday should say the observed day. The Saturday should still say Independence Day.


Last updated: September 18, 2026 | Reading time: 10 minutes

Written by Softery.io, after a month on paper came back with someone else's mark in the corner.