Skip to content

Commit 6ee37f3

Browse files
committed
Allow multiple pages
1 parent ea03545 commit 6ee37f3

File tree

3 files changed

+56
-35
lines changed

3 files changed

+56
-35
lines changed

bot/src/bot.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,14 +324,17 @@ async fn render(
324324

325325
match res {
326326
Ok(res) => {
327-
let image = CreateAttachment::bytes(res.image, "rendered.png");
328-
let mut message = CreateReply::default().attachment(image).reply(true);
327+
let mut message = CreateReply::default().reply(true);
329328

330329
let mut content = String::new();
331330

332-
if let Some(more_pages) = res.more_pages {
333-
let more_pages = more_pages.get();
334-
write!(
331+
if res.images.is_empty() {
332+
writeln!(content, "Note: no pages generated").unwrap();
333+
}
334+
335+
if res.more_pages > 0 {
336+
let more_pages = res.more_pages;
337+
writeln!(
335338
content,
336339
"Note: {more_pages} more page{s} ignored",
337340
s = if more_pages == 1 { "" } else { "s" },
@@ -340,7 +343,7 @@ async fn render(
340343
}
341344

342345
if !res.warnings.is_empty() {
343-
write!(
346+
writeln!(
344347
content,
345348
"Render succeeded with warnings:\n```ansi\n{}\n```",
346349
sanitize_code_block(&res.warnings),
@@ -352,6 +355,11 @@ async fn render(
352355
message = message.content(content);
353356
}
354357

358+
for (i, image) in res.images.into_iter().enumerate() {
359+
let image = CreateAttachment::bytes(image, format!("page-{}.png", i + 1));
360+
message = message.attachment(image);
361+
}
362+
355363
ctx.send(message).await?;
356364
}
357365
Err(error) => {

protocol/src/lib.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::num::NonZeroUsize;
2-
31
use serde::{Deserialize, Serialize};
42

53
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -11,8 +9,8 @@ pub enum Request {
119

1210
#[derive(Debug, Serialize, Deserialize)]
1311
pub struct Rendered {
14-
pub image: Vec<u8>,
15-
pub more_pages: Option<NonZeroUsize>,
12+
pub images: Vec<Vec<u8>>,
13+
pub more_pages: usize,
1614
pub warnings: String,
1715
}
1816

worker/src/render.rs

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::io::Cursor;
2-
use std::num::NonZeroUsize;
32

43
use protocol::Rendered;
54
use typst::eval::Tracer;
@@ -50,6 +49,9 @@ fn to_string(v: impl ToString) -> String {
5049
v.to_string()
5150
}
5251

52+
const PAGE_LIMIT: usize = 5;
53+
const BYTES_LIMIT: usize = 25 * 1024 * 1024;
54+
5355
pub fn render(sandbox: &Sandbox, source: String) -> Result<Rendered, String> {
5456
let world = sandbox.with_source(source);
5557

@@ -58,34 +60,47 @@ pub fn render(sandbox: &Sandbox, source: String) -> Result<Rendered, String> {
5860
typst::compile(&world, &mut tracer).map_err(|diags| format_diagnostics(&world, &diags))?;
5961
let warnings = tracer.warnings();
6062

61-
let frame = &document
63+
let transparent = Color::from_u8(0, 0, 0, 0);
64+
let mut total_attachment_size = 0;
65+
66+
let images = document
6267
.pages
63-
.first()
64-
.ok_or("no pages in rendered output")?
65-
.frame;
66-
let more_pages = NonZeroUsize::new(document.pages.len().saturating_sub(1));
68+
.iter()
69+
.take(PAGE_LIMIT)
70+
.map(|page| {
71+
let frame = &page.frame;
72+
let pixels_per_point = determine_pixels_per_point(frame.size()).map_err(to_string)?;
73+
let pixmap = typst_render::render(frame, pixels_per_point, transparent);
74+
75+
let mut writer = Cursor::new(Vec::new());
76+
77+
// The unwrap will never fail since `Vec`'s `Write` implementation is infallible.
78+
image::write_buffer_with_format(
79+
&mut writer,
80+
bytemuck::cast_slice(pixmap.pixels()),
81+
pixmap.width(),
82+
pixmap.height(),
83+
image::ColorType::Rgba8,
84+
image::ImageFormat::Png,
85+
)
86+
.unwrap();
87+
88+
Ok(writer.into_inner())
89+
})
90+
.take_while(|image| {
91+
if let Ok(image) = image {
92+
total_attachment_size += image.len();
93+
total_attachment_size <= BYTES_LIMIT
94+
} else {
95+
true
96+
}
97+
})
98+
.collect::<Result<Vec<_>, String>>()?;
6799

68-
let pixels_per_point = determine_pixels_per_point(frame.size()).map_err(to_string)?;
100+
let more_pages = document.pages.len() - images.len();
69101

70-
let transparent = Color::from_u8(0, 0, 0, 0);
71-
let pixmap = typst_render::render(frame, pixels_per_point, transparent);
72-
73-
let mut writer = Cursor::new(Vec::new());
74-
75-
// The unwrap will never fail since `Vec`'s `Write` implementation is infallible.
76-
image::write_buffer_with_format(
77-
&mut writer,
78-
bytemuck::cast_slice(pixmap.pixels()),
79-
pixmap.width(),
80-
pixmap.height(),
81-
image::ColorType::Rgba8,
82-
image::ImageFormat::Png,
83-
)
84-
.unwrap();
85-
86-
let image = writer.into_inner();
87102
Ok(Rendered {
88-
image,
103+
images,
89104
more_pages,
90105
warnings: format_diagnostics(&world, &warnings),
91106
})

0 commit comments

Comments
 (0)