Add HTML render format to API and update version to 1.4.8
This commit is contained in:
+46
-5
@@ -3,6 +3,7 @@ use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use typst::diag::{SourceDiagnostic, Warned};
|
||||
use typst::layout::{Frame, FrameItem};
|
||||
use typst_html::HtmlDocument;
|
||||
use typst_layout::PagedDocument;
|
||||
use typst_pdf::{pdf, PdfOptions};
|
||||
use typst_render::{render, RenderOptions};
|
||||
@@ -67,8 +68,8 @@ impl ProjectInput {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_world(self) -> MemoryWorld {
|
||||
MemoryWorld::new_project(self.entrypoint, self.files, self.packages)
|
||||
fn into_world(self, enable_html: bool) -> MemoryWorld {
|
||||
MemoryWorld::new_project(self.entrypoint, self.files, self.packages, enable_html)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ impl TypstCompiler {
|
||||
(Vec<String>, String, DocumentStats),
|
||||
Vec<(SourceDiagnostic, Option<std::ops::Range<usize>>)>,
|
||||
> {
|
||||
let world = input.into_world();
|
||||
let world = input.into_world(false);
|
||||
match typst::compile::<PagedDocument>(&world) {
|
||||
Warned {
|
||||
output: Ok(doc),
|
||||
@@ -127,7 +128,7 @@ impl TypstCompiler {
|
||||
&self,
|
||||
input: ProjectInput,
|
||||
) -> Result<Vec<u8>, Vec<(SourceDiagnostic, Option<std::ops::Range<usize>>)>> {
|
||||
let world = input.into_world();
|
||||
let world = input.into_world(false);
|
||||
match typst::compile::<PagedDocument>(&world) {
|
||||
Warned {
|
||||
output: Ok(doc),
|
||||
@@ -159,7 +160,7 @@ impl TypstCompiler {
|
||||
&self,
|
||||
input: ProjectInput,
|
||||
) -> Result<Vec<u8>, Vec<(SourceDiagnostic, Option<std::ops::Range<usize>>)>> {
|
||||
let world = input.into_world();
|
||||
let world = input.into_world(false);
|
||||
match typst::compile::<PagedDocument>(&world) {
|
||||
Warned {
|
||||
output: Ok(doc),
|
||||
@@ -192,4 +193,44 @@ impl TypstCompiler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export_html(
|
||||
&self,
|
||||
input: ProjectInput,
|
||||
) -> Result<String, Vec<(SourceDiagnostic, Option<std::ops::Range<usize>>)>> {
|
||||
let world = input.into_world(true);
|
||||
let document = match typst::compile::<HtmlDocument>(&world) {
|
||||
Warned {
|
||||
output: Ok(document),
|
||||
warnings: _,
|
||||
} => document,
|
||||
Warned {
|
||||
output: Err(errors),
|
||||
warnings: _,
|
||||
} => {
|
||||
use typst::WorldExt;
|
||||
return Err(errors
|
||||
.into_iter()
|
||||
.map(|d| {
|
||||
let range = world.range(d.span);
|
||||
(d, range)
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
};
|
||||
|
||||
match typst_html::html(&document) {
|
||||
Ok(html) => Ok(html),
|
||||
Err(errors) => {
|
||||
use typst::WorldExt;
|
||||
Err(errors
|
||||
.into_iter()
|
||||
.map(|d| {
|
||||
let range = world.range(d.span);
|
||||
(d, range)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ pub async fn render_handler(
|
||||
None => return (StatusCode::UNAUTHORIZED, "Missing or invalid Authorization header. Use: Authorization: Bearer <api-key>").into_response(),
|
||||
};
|
||||
|
||||
if payload.format != "png" && payload.format != "pdf" {
|
||||
return (StatusCode::BAD_REQUEST, "Invalid format. Must be 'png' or 'pdf'").into_response();
|
||||
if payload.format != "png" && payload.format != "pdf" && payload.format != "html" {
|
||||
return (StatusCode::BAD_REQUEST, "Invalid format. Must be 'png', 'pdf', or 'html'").into_response();
|
||||
}
|
||||
|
||||
if payload.code.trim().is_empty() {
|
||||
@@ -170,7 +170,11 @@ pub async fn render_handler(
|
||||
|
||||
// Check cache
|
||||
let cache_key = compute_cache_key(&payload.format, &payload.code, &payload.files);
|
||||
let content_type: &'static str = if payload.format == "pdf" { "application/pdf" } else { "image/png" };
|
||||
let content_type: &'static str = match payload.format.as_str() {
|
||||
"pdf" => "application/pdf",
|
||||
"html" => "text/html; charset=utf-8",
|
||||
_ => "image/png",
|
||||
};
|
||||
|
||||
if let Ok(Some((data, created_at))) = sqlx::query_as::<_, (Vec<u8>, String)>(
|
||||
"SELECT data, created_at FROM api_render_cache WHERE content_hash = ? AND format = ?"
|
||||
@@ -216,6 +220,9 @@ pub async fn render_handler(
|
||||
let result = match payload.format.as_str() {
|
||||
"pdf" => compiler.export_pdf(ProjectInput::single(payload.code.clone(), files_map)),
|
||||
"png" => compiler.export_png(ProjectInput::single(payload.code.clone(), files_map)),
|
||||
"html" => compiler
|
||||
.export_html(ProjectInput::single(payload.code.clone(), files_map))
|
||||
.map(|html| html.into_bytes()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
drop(compiler);
|
||||
|
||||
+10
-1
@@ -31,6 +31,7 @@ impl MemoryWorld {
|
||||
entrypoint: String,
|
||||
files: HashMap<String, Vec<u8>>,
|
||||
local_packages: HashMap<String, HashMap<String, Vec<u8>>>,
|
||||
enable_html: bool,
|
||||
) -> Self {
|
||||
let main = FileId::new(RootedPath::new(
|
||||
VirtualRoot::Project,
|
||||
@@ -62,8 +63,16 @@ impl MemoryWorld {
|
||||
}
|
||||
}
|
||||
|
||||
let library = if enable_html {
|
||||
Library::builder()
|
||||
.with_features([typst::Feature::Html].into_iter().collect())
|
||||
.build()
|
||||
} else {
|
||||
Library::builder().build()
|
||||
};
|
||||
|
||||
Self {
|
||||
library: typst::utils::LazyHash::new(Library::builder().build()),
|
||||
library: typst::utils::LazyHash::new(library),
|
||||
main,
|
||||
files,
|
||||
local_packages,
|
||||
|
||||
Reference in New Issue
Block a user