Compare commits

...

6 commits

7 changed files with 2822 additions and 6 deletions

View file

@ -637,6 +637,7 @@ const SPLASHES = [
"america is not a developed country",
"an elegant weapon for a more civilized age", // Star Wars (1977)
"an entire warehouse of rose-colored glasses", // Folding Ideas, World of Warcraft Classic https://youtu.be/0RxQRswLAmI?t=24m02s
"an even number of sign errors",
"an unidentified frequency has been existing in the system", // Foremost Poets, Moonraker
"and arriving at a position where it wasn't, it now is", // The Missle https://youtu.be/bZe5J8SVCYQ
"and as always, thanks for watching", // Vsauce, e.g. https://youtu.be/fCn8zs912OE&t=20m04s
@ -684,6 +685,8 @@ const SPLASHES = [
"but stop noddin' your head unless you really agree", // Flobots, Free-Written (2001)
"but wait, there's more",
"but you already knew that",
"buy high, sell low",
"buy the haystack", // John Bogle
"buying is easy and doing is hard",
"by night one way, by day another", // Shrek (2001)
"can be burnt for warmth",
@ -721,6 +724,7 @@ const SPLASHES = [
"could i interest you in everything, all of the time?", // Bo Burnham, Welcome to the Internet https://youtu.be/k1BneeJTDcU
"could you kick up the 4d3d3d3?", // Tim and Eric, Celery Man https://youtu.be/maAFcEU6atk
"couldn't help but notice that strange and interesting plant", // Little Shop of Horrors https://youtu.be/gAbmhnvnolo
"create, read, update, delete", // https://en.wikipedia.org/wiki/Create,_read,_update_and_delete
"crosses the blood-brain barrier",
"d a b f# g d g a", // RobPRocks, Pachelbel Rant https://youtu.be/JdxkVQy7QLM
"deals in absolutes", // Star Wars Episode III: Revenge of the Sith
@ -758,6 +762,7 @@ const SPLASHES = [
"don't be evil", // Since Google is done with this motto I guess I'll use it for a while
"don't bernie me", // The Incredibles (2004)
"don't call it a comeback", // LL Cool J, Mama Said Knock You Out https://youtu.be/vimZj8HW0Kg
"don't dead open inside", // https://knowyourmeme.com/memes/dont-dead-open-inside
"don't even think about going out, coraline jones!", // Coraline (2009)
"don't forget to like, share, and subscribe",
"don't panic, call me and i'll tell you a joke", // Bo Burnham, Comedy https://youtu.be/0GR6QuCf-Ww
@ -777,6 +782,7 @@ const SPLASHES = [
"each one of these stickers adds five horsepower", // Sh_t Civic Owners Say https://youtu.be/f9x74SlY1ik
"embrace, extend, extinguish", // https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish
"en ai marre d'en avoir marre, aussi", // Alizée, J'en Ai Marre https://youtu.be/iCz8R25hlFI
"end road work",
"endorses sponsorblock", // https://sponsor.ajay.app/
"endorses ublock origin", // https://github.com/gorhill/uBlock
"enjoys collecting unspent ordnance",
@ -843,6 +849,7 @@ const SPLASHES = [
"holy breaking and entering, it's batgirl", // Batgirl equal pay PSA https://youtu.be/szZsKdJYR-A
"hopelessly intricate, involved or perplexing",
"how did you almost know my name?", // Adventure Time S01E18 Dungeon https://youtu.be/W9_iQ1FSnp8
"how infinite in faculty", // https://en.wikipedia.org/wiki/What_a_piece_of_work_is_a_man
"how long has it been since your last confession?",
"how would you like a life of luxury and deceit?", // Back to School (1986)
"humbly remains the best website ever made",
@ -950,6 +957,7 @@ const SPLASHES = [
"is sitting in a room different from the one you are in now", // Alvin Lucier, I Am Sitting in a Room https://youtu.be/fAxHlLK3Oyk
"is stimulating my thinker", // Learn to speak body tape 5 https://youtu.be/x9YTxff3pHU
"is subject to the frequency illusion", // https://en.wikipedia.org/wiki/Frequency_illusion
"is sufficiently transformative", // https://en.wikipedia.org/wiki/Fair_use
"is the product of several minutes of hard work",
"is wasting your processor cycles",
"is worth a lot of money",
@ -962,6 +970,7 @@ const SPLASHES = [
"it's a dicer, grater, peeler, all in one!", // The Truman Show (1998)
"it's all right here at your fingertips", // The Kids Guide to the Internet https://youtu.be/mfMrVKnGzwg
"it's been all downhill since the peak",
"it's complicated",
"it's evident that speed's tangential to that time-position curve", // MindofMatthew, I Will Derive https://youtu.be/P9dpTTpjymE
"it's in the taking, making, baking, taking, faking", // System of a Down, Revenga https://youtu.be/qOl7m52TQk8
"it's long-running character hat!dan, the dan with a hat!", // Folding Ideas, Nostalgia Critic and The Wall https://youtu.be/rokAtlFGa7Y?t=47m08s
@ -970,6 +979,7 @@ const SPLASHES = [
"it's the perfect place for an accident", // The Spirit of Dark and Lonely Water https://youtu.be/XNPMYRlvySY
"it's what plants crave",
"it's where i spend the vast majority of my time", // Tim Minchin, Not Perfect (2005) https://youtu.be/dg3PberzvXo
"just be yourself... but not like that!",
"ka-chow", // Cars (2006)
"keikaku means plan", // https://knowyourmeme.com/memes/just-according-to-keikaku
"kept hidden for almost two decades and forced to bear children", // BBC anchor reads teleprompter https://youtu.be/loWFypHb48k
@ -985,6 +995,8 @@ const SPLASHES = [
"later, creator", // Messiah https://youtu.be/nnHLx3U7NfM
"learned how to plow by reading books", // It's Impossible to Learn to Plow by Reading Books (1988)
"leaves nothing but footsteps",
"let me know in the comments",
"let's dispel once and for all with this fiction",
"like a crème caramel in an earthquake", // Crème Caramel https://vimeo.com/97232050
"like roses and clover", // The Chordettes, Mr. Sandman (1954)
"line goes up", // Folding Ideas, The problem with NFTs https://youtu.be/YQ_xWvX1n9g
@ -1144,6 +1156,7 @@ const SPLASHES = [
"that's an ethical line i'm not used to crossing", // GoldVision, 25 Games of Christmas, Minecraft https://youtu.be/QKojcqhOqhM
"the act of seeing with one's own eyes", // https://en.wikipedia.org/wiki/The_Act_of_Seeing_with_One%27s_Own_Eyes
"the angle of incidence is equal to the angle of reflection",
"the back of your head is ridiculous", // Can I Have Your Number https://youtu.be/7eDqIKzmsWs
"the backlash to the backlash to the thing that's just begun", // Bo Burnham, That Funny Feeling https://youtu.be/WPB6u1BqZqU
"the bad change list is a little longer", // Ross's Game Dungeon: Deus Ex - Invisible War https://youtu.be/pPwpLDvAnvo?t=5m09s
"the blood brought me this far", // Hellraiser (1987)
@ -1175,6 +1188,7 @@ const SPLASHES = [
"then i don't need a jacket", // Pretty much everywhere, it's gonna be hot https://youtu.be/7QLSRMoKKS0
"there are many like it, but this one is mine",
"there are simply too many notes", // Amadeus https://youtu.be/hMWy3EpEo_o
"there's a snake in my boot", // Toy Story (1995)
"there's no going back",
"there's nothing in this cave worth dying for",
"they don't think it be like it is, but it do",
@ -1211,6 +1225,7 @@ const SPLASHES = [
"us-dfpad / 393-h-15a", // Local58, Contingency https://youtu.be/3c66w6fVqOI
"used coding and algorithms", // Tech Insider's groundbreaking journalism https://files.voussoir.net/misc/coding%20and%20algorithms.mp4
"useless, but not for long", // Gorillaz, Clint Eastwood https://youtu.be/1V_xRb0x9aw
"valve, please fix",
"veni, vidi, vici", // https://en.wikipedia.org/wiki/Veni,_vidi,_vici
"vibrate your uvula by dampening and undampening your larynx", // Adventure Time S03E10 What Was Missing? (2011)
"view the source, luke",
@ -1222,6 +1237,7 @@ const SPLASHES = [
"watches for the plot",
"we are sorry that we have a breakdown", // https://youtu.be/a_0xLaSQAQ0
"we begin bombing in five minutes", // https://en.wikipedia.org/wiki/We_begin_bombing_in_five_minutes
"we must do something; this is something; we must do this", // https://dwheeler.com/essays/politicians-syllogism.html
"we need you defending us with the mg42", // Ross's Game Dungeon, Wolfenstein https://youtu.be/P4LaR1C6Xds?t=8m55s
"we're not bop-bipping the boop-beeps here, just boppin'", // Ross's Game Dungeon, Boppin' https://youtu.be/YPygM9cBGY0
"we're not savages, we're english", // Lord of the Flies (1954)
@ -1318,6 +1334,7 @@ const SPLASHES = [
"Я пришёл дать эту песню", // Витас, 7 Элемент / Vitas, 7th Element (2001)
"⛧",
"ギミチョコ!!", // BABYMETAL, ギミチョコ!! / Gimme Chocolate!! (2014)
"可愛い子には旅をさせよ",
"四院点灯!", // 大红灯笼高高挂 / Raise the Red Lantern (1991) translation "Light the lanterns at the fourth house!"
"父死、子死、孙死", // Zen parable, the natural order of death.
"萬づ仕舞口が大事と也", // 葉隠 / Hagakure (~1716) "The end is important in all things", see Ghost Dog: Way of the Samurai (1999)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,582 @@
CSS for printing to paper
=========================
## Introduction
At work, one of the things I do pretty often is write print generators in HTML to recreate and replace forms that the company has traditionally done handwritten on paper or in Excel. This allows the company to move into new web-based tools where the form is autofilled by URL parameters from our database, while getting the same physical output everyone's familiar with.
This article explains some of the CSS basics that control how your webpages look when printed, and a couple of tips and tricks I've learned that might help you out.
## Sample files
Here are some sample page generators to establish some context, and perhaps a shred of credibility.
I'll be the first to admit these pages are a little bit ugly and could use more polish. But they get the job done and I'm still employed.
[Invoice generator](sample_invoice.html)
[Coversheet with sidebar inputs](sample_sidebar.html
?jobno=BRAINIACS
&commissioner=Minister%20McMinisterface
&medium=film
&execproducer=Golan%20%26%20Globus
&color=B/W
&director=Peter%20Jackson
&length=80m
&funding=80%%20benefactor;%2020%%20panhandling
&budget=$1,234,567.00
&jobcontactname=Taylor%20Shift
&jobcontactphone=555-123-4567
&jobcontactemail=production@voussoir.net
&unioncontact=Richard%20Pryor
&union=TAAGSRFA
&synopsis=A%20group%20of%20ten%20quirky%20teenagers%20from%20opposite%20ends%20of%20town%20awaken%20one%20morning%20to%20find%20themselves%20in%20an%20unrecognizable%20world.%20All%20their%20friends%20and%20family%20are%20chasing%20them%20with%20sawed-off%20shotguns%20and%20flamethrowers.%20The%20group%20realizes%20they%20need%20to%20overcome%20their%20differences%20and%20work%20together%20if%20they%20want%20to%20have%20any%20chance%20of%20ending%20the%20human%20race%20and%20eating%20all%20the%20brains.%20Rated%20G.
&principlephoto=2024-06-01
&wrapdate=2024-06-03
&talent=Molly%20Ringwald%20as%20Claire%0AAngus%20Scrimm%20as%20Tiny%20Tim%0ASteve%20McQueen%20as%20Chadster%0AKeith%20David%20as%20Frank%20Armitage%0Alocal%20pigeon%20as%20The%20Great%20Destroyer%0AMichael%20Beck%20as%20Swan%0ADavid%20Cronenberg%20as%20himself
&payfreq=weekly
&otrate=1.1x
&dtrate=1.15x
&payrollcontact=shredpile@voussoir.net
&remarks=Please%20try%20to%20get%20Tom%20Cruise%20in%20the%20promotional%20material%20because%20we%20need%20something%20to%20draw%20crowds.
&signedby=voussoir
&signeddate=2024-02-29
)
[Coversheet with contenteditable](sample_contenteditable.html
?missionnumber=ABC123
&victimname=Minister%20McMinisterface
&dob=1970-01-01
&occupation=Prime%20Minister%20of%20Governmentown
&maritalstatus=Divorced%20once%20removed
&language=en_CA-eh
&children=9
&issuedate=2024-02-27
&duedate=2024-07-04
&customerrequest=I%20want%20McMinisterface%20to%20experience%20a%20pranking%20the%20likes%20of%20which%20he%27s%20never%20seen%20and%20will%20never%20see%20again
&catalogno=GOT-UR-NOSE
&catalogvar=yogababe
&catalogsubvar=downwarddog
&specifics=At%20precisely%200604%20hours,%20as%20McMinisterface%20is%20heading%20out%20the%20front%20door%20of%20his%20flat%20carrying%20a%20breakfast%20scone%20and%20that%20stupid%20yellow%20coffee%20mug,%20the%20Operative%20shall%20place%20himself%20in%20front%20of%20the%20door%20of%20the%20flat,%20assuming%20the%20Downward%20Dog%20position,%20rendering%20him%20undetectable%20to%20all%20passersby.%20McMinisterface%20will%20unexpectedly%20trip%20over%20the%20firmly%20planted%20body%20of%20the%20Operative,%20landing%20on%20the%20concrete,%20breaking%20the%20cartilage%20in%20his%20nose%20and%20dropping%20that%20stupid%20yellow%20coffee%20mug.%20The%20Operative%20will%20approach%20the%20fallen%20McMinisterface%20and%20offer%20to%20help%20him%20up,%20but%20at%20the%20last%20minute%20say%20YOINK,%20GOT%20YOUR%20NOSE%20and%20run%20towards%20the%20bus%20station%20at%20the%20corner%20of%20First%20and%201st%20to%20catch%20the%200606%20bus%20for%20a%20timely%20escape.
&callsick=1
&leavephone=1
&scheduleddelivery=Anti-itch%20ointment,%20to%20be%20left%20on%20porch%20because%20too%20embarrassed%20to%20make%20knowing%20eye%20contact%20with%20delivery%20person
&leaveotherphone=1
&leaveotherotherphone=0
&soundeffects=snoring;%20groaning;%20leavemealone%20120min%20loop
&jointeffort=0
&jointopname=NA
&jointlocation=NA
&jointtime=NA
&leavehome=0430
&leavecafe=0545
&arrivesite=0602
&getaway=0606
&laylow=72hr%20minimum
)
[QR code generator](sample_qr.html)
## @page
CSS has a rule called [`@page`](https://developer.mozilla.org/en-US/docs/Web/CSS/@page) that informs the browser of your website's printing preferences. Normally, I use
```CSS
@page
{
size: Letter portrait;
margin: 0;
}
```
I will explain why I choose `margin: 0` in the later section about margins. You should use Letter or A4 as appropriate for your relationship with the metric system.
Setting the size and margin of @page is not the same as setting the width, height, and margin of your `<html>` or `<body>` element. @page is beyond the DOM -- it contains the DOM. On the web, your `<html>` element is bounded by the edges of your screen, but when printing it is bounded by @page.
The settings controlled by @page more or less correspond to the settings you get in your browser's print dialog when you press Ctrl+P.
Here's a sample file I used to do some experiments:
```HTML
<!DOCTYPE html>
<html>
<style>
@page
{
/* see below for each experiment */
}
html
{
width: 100%;
height: 100%;
background-color: lightblue;
/* grid by shunryu111 https://stackoverflow.com/a/32861765/5430534 */
background-size: 0.25in 0.25in;
background-image:
linear-gradient(to right, gray 1px, transparent 1px),
linear-gradient(to bottom, gray 1px, transparent 1px);
}
</style>
<body>
<h1>Sample text</h1>
<p>sample text</p>
</body>
</html>
```
Here's how that looks in the browser:
![](sample_in_browser.png "but i don't want new chrome")
And here are the results of some different @page values:
`@page { size: Letter portrait; margin: 1in; }`:
![](letter_portrait_1in.png)
`@page { size: Letter landscape; margin: 1in; }`:
![](letter_landscape_in1.png)
`@page { size: Letter landscape; margin: 0; }`:
![](letter_landscape_0.png)
Setting the @page size won't actually put that size of paper into your printer's feed tray. [You'll have to do that part yourself](https://youtu.be/3z9YjGWaxC0 "Office Space - PC Load Letter?").
Notice how when I set `size` to A5, my printer stays on Letter, and the A5 size fits entirely within the Letter size which gives the appearance of a margin even though it's not coming from the `margin` setting.
`@page { size: A5 portrait; margin: 0; }`:
![](a5_portrait_0.png)
But if I tell the printer that I have actual A5 paper loaded, then it looks as expected.
![](a5_portrait_0_a5paper.png)
From what I gather by experimentation, Chrome only follows the @page rule if you have Margin set to Default. As soon as you change Margin in the print dialog, your output is instead the product of your physical paper size and the chosen margin.
`@page { size: A5 portrait; margin: 0; }`:
![](a5_portrait_0_default.png)
![](a5_portrait_0_none.png)
Even when you choose a @page size that fits fully within your physical paper, the `margin` still matters. Here, I make a 5x5 square with no margin, and a 5x5 square with margin. The size of the `<html>` element is bounded by the @page size **and** margin combined.
`@page { size: 5in 5in; margin: 0; }`:
![](5in_5in_0.png)
`@page { size: 5in 5in; margin: 1in; }`:
![](5in_5in_1in.png)
I did all these tests not because I expect to print on A5 or 5x5 paper, but because it took me a while to figure out what exactly @page is. Now I am pretty confident in always using Letter with margin 0.
## @media print
There is a [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media) called `print` where you can write styles that only apply during printing. My generator pages often contain a header, some options, and some help text for the user that obviously shouldn't come out on the print, so this is where you add `display:none` on those elements.
```CSS
/* Normal styles that appear while you are preparing the document */
header
{
display: block;
}
@media print
{
/* Disappear when you are printing the document */
header
{
display: none;
}
}
```
![](mediaprint_1.png)
![](mediaprint_2.png)
## Width, height, margin, and padding
You'll need to know a bit about the [box model](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model) to get the margins you want without wrestling the computer too much.
[![](box_model.png)](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model)
The reason I always set @page `margin: 0` is that I'd rather handle the margins on the DOM elements instead. When I tried to use @page `margin: 0.5in`, I would often accidentally wind up with double-margins that squash the content smaller than I expected, and my one-page design spilled onto a second page.
If I wanted to use @page margin, then the actual page content would need to be laid out all the way up against the edges of the DOM, which is harder for me to think about and harder to preview before printing. It is mentally easier for me to remember that `<html>` occupies the entire physical paper and my margins are within the DOM instead of beyond it.
```CSS
@page
{
size: Letter portrait;
margin: 0;
}
html,
body
{
width: 8.5in;
height: 11in;
}
```
When it comes to multi-page print generators, you're going to want a separate DOM element representing each page. Since you can't have multiple `<html>` or `<body>`, you're going to need another element. I like `<article>`. Even for single-page generators, you may as well always use an article.
Since each `<article>` represents one page, I don't want any margins or padding on `<html>` or `<body>`. We're pushing the logic one step further -- it is easier for me to let the article occupy the entire physical page and put my margins within it.
```CSS
@page
{
size: Letter portrait;
margin: 0;
}
html,
body
{
margin: 0;
}
article
{
width: 8.5in;
height: 11in;
}
```
When I talk about adding margin within my article, I'm not using the `margin` property, I'm using `padding`. That's because `margin` goes outside and around your element in the box model. If you use a `margin` of 0.5in, you'll have to set the article to 7.5&times;10 so that the article plus 2&times;margin equals 8.5&times;11. And if you want to adjust that margin you'll have to adjust the other dimensions.
Instead, `padding` goes on the inside of the element, so I can define the article to be 8.5&times;11 with 0.5in padding, and all the elements inside the article will stay on the page.
A lot of intuition about element dimensions is easier when you set [`box-sizing: border-box`](https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing). It makes it so that the outer dimensions of the article are locked in while you adjust the inner padding. This is my snippet:
```CSS
html
{
box-sizing: border-box;
}
*, *:before, *:after
{
box-sizing: inherit;
}
```
Let's put this all together:
```CSS
@page
{
size: Letter portrait;
margin: 0;
}
html
{
box-sizing: border-box;
}
*, *:before, *:after
{
box-sizing: inherit;
}
html,
body
{
margin: 0;
}
article
{
width: 8.5in;
height: 11in;
}
```
![](margins_padding.png)
## Element positioning
Once you've got your articles and margins set up, the space inside the article is yours to do with as you please. Design your document using whatever HTML/CSS you feel is appropriate for the project. Sometimes this means laying out elements with flex or grid because you've been given some leeway with the output. Sometimes it means creating squares of a specific size to fit on a certain brand of sticker paper. Sometimes it means absolutely positioning absolutely everything to the millimeter because the user needs to feed a special piece of pre-labeled paper through the printer to get your data on top of it, and you're not in control of that special paper.
I'm not here to give a tutorial on how to write HTML in general, so you'll need to be able to do that. All I can say is be mindful of that fact that you're dealing with the limited real estate of a piece of paper, unlike a browser window which can scroll and zoom to any length or scale. If your document will contain an arbitrary number of items, be ready to paginate by creating more `<article>`.
## Multi-page documents with repeating elements
A lot of the print generators I write contain tabular data, like an invoice full of line items. If your `<table>` is large enough to go onto a second page, the browser will automatically duplicate the `<thead>` at the top of each page.
```HTML
<table>
<thead>
<tr>
<th>Sample text</th>
<th>Sample text</th>
</tr>
</thead>
<tbody>
<tr><td>0</td><td>0</td></tr>
<tr><td>1</td><td>1</td></tr>
<tr><td>2</td><td>4</td></tr>
...
</tbody>
</table>
```
![](multipage_table_1.png)
That's great if you're just printing a `<table>` with no frills, but in a lot of real scenarios it's not that simple. The document I'm recreating often has a letterhead on the top of each page, a footer on the bottom, and other custom elements that need to be explicitly repeated on each page. If you just print a single long table across pages, you don't have much ability to place other elements above, below, and around it on intermediate pages.
So, I generate the pages using javascript, splitting the table into several smaller ones. The general approach here is this:
1. Treat the `<article>` elements as disposable and be ready to regenerate them at any time from objects in memory. All user input and configuration should take place in a separate header / options box, outside of the articles.
1. Write a function called `new_page` that creates a new article element with the necessary repeating header/footer/etc.
1. Write a function called `render_pages` that creates the articles from the base data, calling `new_page` every time it fills up the previous one. I usually use `offsetTop` to see when the content is getting far along the page, though you could **definitely** use smarter techniques to get the perfect fit on each page.
1. Call `render_pages` whenever the base data changes.
```Javascript
function delete_articles()
{
for (const article of Array.from(document.getElementsByTagName("article")))
{
document.body.removeChild(article);
}
}
function new_page()
{
const article = document.createElement("article");
article.innerHTML = `
<header>...</header>
<table>...</table>
<footer>...</footer>
`;
document.body.append(article);
return article;
}
function render_pages()
{
delete_articles();
let page = new_page();
let tbody = page.query("table tbody");
for (const line_item of line_items)
{
// I usually pick this threshold by experimentation but you can probably
// do something more rigorously correct.
if (tbody.offsetTop + tbody.offsetParent.offsetTop > 900)
{
page = new_page();
tbody = page.query("table tbody");
}
const tr = document.createElement("tr");
tbody.append(tr);
// ...
}
}
```
It is usually good to include a "page X of Y" counter on your pages. Since the number of pages is not known until all pages are generated, I can't do this during the for loop. I call a function like this at the end:
```Javascript
function renumber_pages()
{
let pagenumber = 1;
const pages = document.getElementsByTagName("article");
for (const page of pages)
{
page.querySelector(".pagenumber").innerText = pagenumber;
page.querySelector(".totalpages").innerText = pages.length;
pagenumber += 1;
}
}
```
## Portrait / Landscape mode
I've shown that the @page rule helps inform the browser's default print settings, but the user can override it if they want to. If you set @page to portrait mode and the user overrides it to landscape mode, your layout and pagination might look wrong, especially if you are hardcoding any page thresholds.
You can accommodate them by creating separate `<style>` elements for portrait and landscape, and using javascript to switch between them. There might be a better way to do this, but at-rules like @page behave differently than normal CSS properties so I'm not sure. You should also save some variable that can help your `render_pages` function do the right thing.
You could also stop hardcoding thresholds, but then I'd have to follow my own advice.
```HTML
<select onchange="return page_orientation_onchange(event);">
<option selected>Portrait</option>
<option>Landscape</option>
</select>
```
```HTML
<style id="style_portrait" media="all">
@page
{
size: Letter portrait;
margin: 0;
}
article
{
width: 8.5in;
height: 11in;
}
</style>
<style id="style_landscape" media="not all">
@page
{
size: Letter landscape;
margin: 0;
}
article
{
width: 11in;
height: 8.5in;
}
</style>
```
```Javascript
let print_orientation = "portrait";
function page_orientation_onchange(event)
{
print_orientation = event.target.value.toLocaleLowerCase();
if (print_orientation == "portrait")
{
document.getElementById("style_portrait").setAttribute("media", "all");
document.getElementById("style_landscape").setAttribute("media", "not all");
}
if (print_orientation == "landscape")
{
document.getElementById("style_landscape").setAttribute("media", "all");
document.getElementById("style_portrait").setAttribute("media", "not all");
}
render_printpages();
}
function render_printpages()
{
if (print_orientation == "portrait")
{
// ...
}
else
{
// ...
}
}
```
## Data source
There are a couple of ways to get your data onto the page. Sometimes, I pack all of the data into the URL parameters, so the javascript just does `const url_params = new URLSearchParams(window.location.search);` and then a bunch of `url_params.get("title")`. This has some advantages:
- The page loads very fast.
- It's easy to debug and experiment by changing the URL.
- The generator works offline.
This also has some disadvantages:
- The URLs become very long and unweildy, people cannot comfortably email them to each other. See sample links at the top of this article.
- If the URL does get sent in an email, that data is "locked in", even if the source record in your database changes later.
- Browsers do have limits on URL length. The limits are pretty high but not infinite and might vary per client.
Sometimes I instead use javascript to fetch our database records over the API, so the URL parameters just contain the record's primary key and maybe a mode setting.
This has some advantages:
- The URLs are much shorter.
- The data is always fresh.
and disadvantages:
- The user has to wait a second while the data is being fetched.
- You have to write more code.
Sometimes I set [`contenteditable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable) on the articles so the user can make small changes before printing. I also like to use real, live checkbox inputs they can click before printing. These features add some convenience, but in most cases it would be wiser to make the user change the source record in the database first. Also, they limit your ability to treat the article elements as disposable.
## Essentials cheatsheet
[sample_cheatsheet.html](sample_cheatsheet.html)
```HTML
<!DOCTYPE html>
<html>
<style>
@page
{
size: Letter portrait;
margin: 0;
}
html
{
box-sizing: border-box;
}
*, *:before, *:after
{
box-sizing: inherit;
}
html,
body
{
margin: 0;
background-color: lightblue;
}
header
{
background-color: white;
max-width: 8.5in;
margin: 8px auto;
padding: 8px;
}
article
{
background-color: white;
padding: 0.5in;
width: 8.5in;
height: 11in;
/* For centering the page on the screen during preparation */
margin: 8px auto;
}
@media print
{
html,
body
{
background-color: white !important;
}
body > header
{
display: none;
}
article
{
margin: 0 !important;
}
}
</style>
<body>
<header>
<p>Some help text to explain the purpose of this generator.</p>
<p><button onclick="return window.print();">Print</button></p>
</header>
<article>
<h1>Sample page 1</h1>
<p>sample text</p>
</article>
<article>
<h1>Sample page 2</h1>
<p>sample text</p>
</article>
</body>
</html>
```

View file

@ -3,6 +3,186 @@ Cyborgs on HN
This page collects comments which make unnecessary or tenuous analogies to computers, programming, dollar-sign $variables, sed's/replace/syntax/g, mathematics, AI/machine learning, and cryptography in discussions that aren't about those things.
> Agreed, but that's a two part issue.
> \#1 - The modern web has a much lower ratio of citizenCreators : consumers than the early web.
> \#2 - The utility of information is professionalUnbiased \> proHobbyist \> professionalBiased \> randomPerson \>\> SEO spam.
> Google's current ranking is SEO spam \>\> professionalBiased \> professionalUnbiased \>\> proHobbyist \> randomPerson.
> That's a clear misalignment.
-
> Sadly, due to being on the first floor\* I don't have a lawn.
> I do keep considering balcony mounts for what would've been the lawn defence cannon though.
> \* en_UK first floor, mind, I know en_US insists on 1-based indexing rather than 0-based for floors but I don't particularly like writing FORTRAN either.
-
> s/at the speed of light/no faster than the speed of light/
-
> Also, what is with this misconception: "When twins arent being regarded as carbon copies, they are slotted (or slot themselves) into opposing roles." Dude, any vector can be projected onto another one, with an orthogonal ("opposing") component left over.
-
> Layoffs in the WFH era are weird. Back in the day you had a pretty good idea of who got laid off because you saw them walking out the door with a box of their stuff. You could go up to them and say, "hey let's meet at $local_watering_hole and hang out". You could swap contact info if you didn't already have it.
-
> On far older hardware, we still used spinning rust for $deity sake...
-
> Once you understand the embryology, there are actually a variety of things like the appendix: they're just the ends of things that come together. The uvula is a great example: embryologically, the two sides of the face separate very early as an opening, then develop a lot of complicated anatomy and fuse back together. The uvula is where that fusion completes and the fusion process completes (returns 0) when the microenvironment variables get to a certain state.
-
> The ability to shutdown any persons the government doesn't like using any growing list of $Excuses is worrying.
-
> I have a couple of examples (of HN and high-ranking HNers natch ;) and found a fair number of bugs\^H\^H\^H\^H non-optimal choices in their feeds, probably because XML is not all that human-readable, even for devs.
-
> I will admit, I've often searched for $project_name github to get to their repositories. It shouldn't matter but it's just a force of habit now.
-
> Xiaomi is China's Apple, so what happened to the rumored Apple car? Haven't heard anything for a while, but Wikipedia claims it's still planned for 2024-25:
> > Yeah, under the hood that date is {{Date().getFullYear() + 1}}
> -
> > Calling it: Apple will partner with an established automaker such as Goldman Sachs\^H\^H\^H\^H\^H\^H\^H\^H\^H\^H\^H\^H\^HPolestar and customize the infotainment OS.
-
> Inc actually does this sort of madlibs article all the time
> "In $NUMBER (words/sentences), $FOO taught a masterclass in $BAR"
-
> You're positing that some significant portion of the victims decide to settle like this:
> if (shame)
> But it's almost certainly this:
> if (bankruptcy_risk || shame)
-
> I actually don't benefit at all from you using my code; I did all the work to make the world a place where $PROBLEM has an accessible solution.
-
> At $DAY_JOB we recently scuttled most development efforts for a week for our teams. Our nightly backup job that sanitizes PHI ballooned overtime to, say, 20GB+1Byte and ran out of disk space. Because we are running Kubernetes on Fargate we dont need a full time operations guy, right?
-
> I'm ashamed to admit I often wonder about this when playingˆHˆHˆHˆHˆHˆHˆH my daughter plays with myˆHˆH her Duplo train.
-
> I have no dog in this fight as I rarely deal with cash and do not run a retail business. However from a conceptual point of view I really have a viscerally negative reaction to not accepting cash. It is a societal acceptance of the rent seeking behavior of the credit processing networks. I already have to pay a sales tax to ${TAX_ENTITIES.length} different government entities, I don't love that I also have a 3% tax going to VISA/Mastercard/Amex/...
-
> Anyone who uses ChatGPT for a while will learn to take everything with a grain of salt.
> > s/Grain/huge rock/
-
> Addendum: dont rely on your git/wiki/document-system for tracking the timestamp. "This was accurate on $DATE" has a very different meaning than "this document was last edited on $DATE". It might be 2 years out of date but someone fixed a typo last week.
-
> or s/bottled water/water bottles
-
> Also, adding a charger for your swappable-battery car costs pennies, so lots of people won't even use their car's battery a swapping feature (which is really bad for battery swapping outlets). If most people only use the battery-swap at Christmas and $HOLIDAY, then the networks could be underprovisioned during those spikes and overprovisioned in the other 363 days of the year.
-
> Horrible edge cases to consider when dealing with music
> > title += " publishing metadata"
-
> That video is wrong. Men who are actually friends don't tell each other "that's a nice tie", they tell each other "nice tie $SLUR".
-
> struggles with too many files. Last I looked, official Microsoft documentation says to not exceed 100k files. Yet, $WORK wants to dump basically every file I touch inside there. My Teams folder alone is \~20k files.
-
> Tuta became understandably a walled garden that my other tools couldn't easily work with. This wasted alot of my time.
> Nowadays using $normalemailprovider with Thunderbird.
-
> But having a favourite release of Mac OSX means it's Snow Leopard. I've seen comparisons between $current and $current_minus_one, but never in regards to "That was my favourite" - likely because anyone invested enough in Mac OS releases to have a favourite, favours Snow Leopard.
-
> Sure. But the $CURRENT_MODEL's price never decreases. It stays steady - until the next price bump. E.g. $MID_RANGE_GPU, $HIGH_END_CPU - their price will decrease next year, when the new model comes out. The new model's price will not be lower than the current one.
-
> I now guess somebody wrote it up, published it and maybe linked to it on Twitter, some content-farming bots notice $ACTRESS_NAME trending on Twitter, and their algorithm realize there's audience eyeballs to be gained there, and found the freshest article about her, on Twitter I suppose?
-
> s/China's TikTok users/Users of China's TikTok/
-
> Also starting to realise that my "they can't stop me gaming when I'm 80!" attitude will be self-limited by joint pain at some point... probably in exactly the same way as previous generations, just s/gaming/popular activity of the era/
-
> Calm down satan. Until you manage to manufacture physical products for free (one time cost then get inf copies, as digital content) retail theft !== digital pirating
-
> From someone that occasionally visits YouTube, hearing people talk about YouTube this and YouTube that numerous times in one conversation let alone in a day is very noticeable. Same thing goes for Reddit. Or TikTok. Or any $socialMediaPlatform.
-
> It's been a while now since I used a self-checkout, but my general philosophy is "Sorry, $Employer pays me to fight with buggy, UI-from-hell computers at $Job. If you want me to fight with your self-checkout system, my rate is \$$Lots/hour".
-
> s/Spending/Stocking Up/
-
> I'd pinged HN mods about this, though dang ended up dong a wholesale replacement rather than s/She/Sabine Hossenfelder/.
-
> It really does sound weird. I already imagine trying to sell this at my $DAYJOB (I would likely have to "fork" it myself just to replace a few strings in the codebase that mention the original name).
-
> Ask HN: What programming languages are you using at $WORK?
-
> If you search "watch $MOVIE free" on google you're going to get netflix, Hulu, prime, Disney etc as the first results regardless of whether those sites even have it in their library.
-

View file

@ -293,6 +293,7 @@ https://github.com/richleland/pygments-css
.highlight .vm { color: var(--color_monokai_white) } /* Name.Variable.Magic */
.highlight .il { color: var(--color_monokai_purple) } /* Literal.Number.Integer.Long */
.highlight.css .nc { color: var(--color_monokai_green) }
.highlight.html .na { color: var(--color_monokai_green) } /* Name.Attribute */
.highlight.html .nc { color: var(--color_monokai_green) } /* Name.Attribute */
.highlight.html .o { color: var(--color_monokai_white) } /* Operator */

View file

@ -236,6 +236,8 @@ class Article:
</ul>
<address>Contact me: writing@voussoir.net</address>
<p>If you would like to subscribe for more, add this to your RSS reader: <a rel="alternate" type="application/atom+xml" href="/writing/writing.atom">https://voussoir.net/writing/writing.atom</a></p>
''').render(
github_history=github_history,
commits=commits,
@ -451,11 +453,6 @@ def write_writing_index():
{% endfor %}
</ol>
<p>
<a rel="alternate" type="application/atom+xml" href="/writing/writing.atom">Atom</a> /
<a rel="alternate" type="application/rss+xml" href="/writing/writing.rss">RSS</a>
</p>
<h2>Recently edited</h2>
<ol class="article_list">
{% for article in articles_edited %}
@ -471,6 +468,9 @@ def write_writing_index():
<p>I greatly appreciate the time you have taken to visit my page. If you
have feedback, corrections, or tales of harrowing adventure, send an email
to writing@voussoir.net.
<p>If you would like to subscribe for more, add this to your RSS reader: <a rel="alternate" type="application/atom+xml" href="/writing/writing.atom">https://voussoir.net/writing/writing.atom</a></p>
</article>
</body>
</html>

View file

@ -55,6 +55,8 @@ As a result of writing this article, I have decided to try going back to graph p
- Lightness of gridlines must be tuned per-printer by experimentation.
- The paper may become slightly curled or wavy due to the heat and rollers -- the paper will need time and/or weight to flatten back out.
Treat yourself to heavier paper stock. The cheapest printer paper is usually [20lb or 75gsm](https://en.wikipedia.org/wiki/Grammage). Try something heavier like 28lb/105gsm or 32lb/120gsm. Heavier paper costs more, but it feels more pleasant to write on. If you enjoy it more, you'll do it more.
Consider re-writing your notes after your class/meeting, so that you can:
- Reorganize the information in a new order that you could not have foreseen when taking it down.
@ -71,7 +73,7 @@ I have never used a digital tablet for notetaking, and I will not make the appea
- Infinite paper and ink.
- Undo button, layers, drag to rearrange.
Advantages of paper:
But paper has its own advantages:
- Cheap.
- Easier to lay out multiple sheets and review / cross-reference pages in any order -- not limited to real estate of one screen and scrolling / tab-switching.