Add xkcd document

This commit is contained in:
Joscha 2025-03-18 19:53:35 +01:00
parent f8076e6b66
commit 618f298cc6
13 changed files with 590 additions and 13 deletions

View file

@ -0,0 +1,6 @@
{
"number": 3064,
"title": "Lungfish",
"alt": "I know having so many base pairs makes rebasing complicated, but you're in Bilateria, so shouldn't you at LEAST be better at using git head?",
"feed": false
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View file

@ -0,0 +1 @@
../lib

View file

@ -0,0 +1,26 @@
#import "lib/main.typ" as lib;
#show: it => lib.init(it)
#let data = json("data.json")
#align(center)[
xkcd #data.number
#v(-12pt)
#text(size: 32pt, data.title)
]
// If the image is an odd number of pixels wide, we need to add an extra row of
// pixels (in this case, on the right) to ensure that the image pixels fall on
// screen pixels.
#context {
let img = image("image.png")
let width = measure(img).width
let additional = 2pt * calc.fract(width.pt() / 2)
align(center, stack(dir: ltr, img, h(additional)))
}
#align(center, data.alt)
#if data.feed {
lib.feed
}

View file

@ -0,0 +1,76 @@
use std::io::Cursor;
use anyhow::Context;
use axum::{
Form,
extract::State,
response::{IntoResponse, Response},
};
use image::ImageFormat;
use serde::{Deserialize, Serialize};
use crate::server::{Server, somehow};
#[derive(Deserialize)]
struct ComicInfo {
num: u32,
title: String,
alt: String,
img: String,
}
#[derive(Serialize)]
struct Data {
number: u32,
title: String,
alt: String,
feed: bool,
}
#[derive(Deserialize)]
pub struct FormData {
pub number: Option<u32>,
pub feed: Option<bool>,
}
pub async fn post(server: State<Server>, Form(form): Form<FormData>) -> somehow::Result<Response> {
let client = reqwest::Client::builder()
.user_agent(crate::USER_AGENT)
.build()?;
let url = match form.number {
None => "https://xkcd.com/info.0.json".to_string(),
Some(number) => format!("https://xkcd.com/{number}/info.0.json"),
};
let info = client.get(url).send().await?.json::<ComicInfo>().await?;
let image_data = client.get(&info.img).send().await?.bytes().await?;
let image = image::load_from_memory(&image_data)?.into_rgba8();
let max_width = Some(384);
let max_height = Some(1024);
let image = super::image::dither(image, max_width, max_height, false, "stucki")
.map_err(somehow::Error)?;
let data = Data {
number: info.num,
title: info.title,
alt: info.alt,
feed: form.feed.unwrap_or(true),
};
let mut bytes: Vec<u8> = Vec::new();
image
.write_to(&mut Cursor::new(&mut bytes), ImageFormat::Png)
.context("failed to encode image as png")
.map_err(somehow::Error)?;
let typst = super::typst_with_lib()
.with_json("/data.json", &data)
.with_file("/image.png", bytes)
.with_main_file(include_str!("main.typ"));
server.print_typst(typst).await?;
Ok(().into_response())
}