From 5ef332caa46f1bcb3ac4d63504cceda3900e9ffd Mon Sep 17 00:00:00 2001 From: Laurenz Stampfl Date: Sun, 20 Jul 2025 00:13:25 +0200 Subject: [PATCH] Require the `PdfEmbedding` feature --- .../typst-library/src/visualize/image/mod.rs | 58 +++++++++++-------- crates/typst-render/src/image.rs | 13 ++--- crates/typst-svg/src/image.rs | 30 +++++----- 3 files changed, 51 insertions(+), 50 deletions(-) diff --git a/crates/typst-library/src/visualize/image/mod.rs b/crates/typst-library/src/visualize/image/mod.rs index e9b6df993..bbe6a5639 100644 --- a/crates/typst-library/src/visualize/image/mod.rs +++ b/crates/typst-library/src/visualize/image/mod.rs @@ -15,7 +15,7 @@ use std::fmt::{self, Debug, Formatter}; use std::sync::Arc; use ecow::{eco_format, EcoString}; -use typst_library::World; +use typst_library::{Feature, World}; use typst_syntax::{Span, Spanned}; use typst_utils::LazyHash; @@ -271,32 +271,42 @@ impl Packed { .within(loaded)?, ), ImageFormat::Vector(VectorFormat::Pdf) => { - let document = - PdfDocument::new(loaded.data.clone(), engine.world.clone()) - .within(loaded)?; - let page_num = self.page.get(styles); + if engine.world.library().features.is_enabled(Feature::PdfEmbedding) { + let document = + PdfDocument::new(loaded.data.clone(), engine.world.clone()) + .within(loaded)?; + let page_num = self.page.get(styles); - if page_num == 0 { + if page_num == 0 { + bail!( + span, + "{page_num} is not a valid page number"; + hint: "page numbers for PDF start at 1" + ) + }; + + // The user provides the page number start from 1, further down the pipeline, + // page numbers are 0-based. + let page_idx = page_num - 1; + let num_pages = document.len(); + + let Some(pdf_image) = PdfImage::new(document, page_idx) else { + bail!( + span, + "page {page_num} doesn't exist"; + hint: "the document only has {num_pages} pages" + ); + }; + + ImageKind::Pdf(pdf_image) + } else { bail!( span, - "{page_num} is not a valid page number"; - hint: "page numbers start at 1" - ) - }; - - let page_idx = page_num - 1; - let num_pages = document.len(); - // The user provides the page number staring from 1, further down the pipeline they page - // numbers are 0-based. - let Some(pdf_image) = PdfImage::new(document, page_idx) else { - bail!( - span, - "page {page_num} doesn't exist"; - hint: "the document only has {num_pages} pages" - ) - }; - - ImageKind::Pdf(pdf_image) + "embedding PDFs is currently an experimental, opt-in feature"; + hint: "enable the corresponding feature to try it out"; + hint: "convert your PDF to SVG instead" + ); + } } }; diff --git a/crates/typst-render/src/image.rs b/crates/typst-render/src/image.rs index 1e0ca5941..9ddc07ec8 100644 --- a/crates/typst-render/src/image.rs +++ b/crates/typst-render/src/image.rs @@ -116,9 +116,7 @@ fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option { StandardFont::Helvetica => sf.helvetica.normal.clone(), StandardFont::HelveticaBold => sf.helvetica.bold.clone(), StandardFont::HelveticaOblique => sf.helvetica.italic.clone(), - StandardFont::HelveticaBoldOblique => { - sf.helvetica.bold_italic.clone() - } + StandardFont::HelveticaBoldOblique => sf.helvetica.bold_italic.clone(), StandardFont::Courier => sf.courier.normal.clone(), StandardFont::CourierBold => sf.courier.bold.clone(), StandardFont::CourierOblique => sf.courier.italic.clone(), @@ -132,8 +130,7 @@ fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option { }; bytes.map(|d| { - let font_data: Arc + Send + Sync> = - Arc::new(d.clone()); + let font_data: Arc + Send + Sync> = Arc::new(d.clone()); font_data }) @@ -142,9 +139,7 @@ fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option { let interpreter_settings = InterpreterSettings { font_resolver: Arc::new(move |query| match query { FontQuery::Standard(s) => select_standard_font(*s), - FontQuery::Fallback(f) => { - select_standard_font(f.pick_standard_font()) - } + FontQuery::Fallback(f) => select_standard_font(f.pick_standard_font()), }), warning_sink: Arc::new(|_| {}), }; @@ -156,7 +151,7 @@ fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option { width: Some(w as u16), height: Some(h as u16), }; - + let hayro_pix = hayro::render(page, &interpreter_settings, &render_settings); sk::Pixmap::from_vec(hayro_pix.take_u8(), IntSize::from_wh(w, h)?) diff --git a/crates/typst-svg/src/image.rs b/crates/typst-svg/src/image.rs index d2400ac62..12a5683a5 100644 --- a/crates/typst-svg/src/image.rs +++ b/crates/typst-svg/src/image.rs @@ -1,12 +1,14 @@ -use std::borrow::Cow; -use std::sync::Arc; use base64::Engine; use ecow::{eco_format, EcoString}; use hayro::{FontData, FontQuery, InterpreterSettings, RenderSettings, StandardFont}; use image::{codecs::png::PngEncoder, ImageEncoder}; +use std::borrow::Cow; +use std::sync::Arc; use typst_library::foundations::Smart; use typst_library::layout::{Abs, Axes}; -use typst_library::visualize::{ExchangeFormat, Image, ImageKind, ImageScaling, PdfImage, RasterFormat}; +use typst_library::visualize::{ + ExchangeFormat, Image, ImageKind, ImageScaling, PdfImage, RasterFormat, +}; use crate::SVGRenderer; @@ -73,20 +75,19 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString { // doesn't exceed 3000 pixels. const MIN_WIDTH: f32 = 1000.0; const MAX_WIDTH: f32 = 3000.0; - - + let base_width = pdf.width(); let w_scale = (MIN_WIDTH / base_width).max(MAX_WIDTH / base_width); let base_height = pdf.height(); let h_scale = (MIN_WIDTH / base_height).min(MAX_WIDTH / base_height); - + let total_scale = w_scale.min(h_scale); - + let width = (base_width * total_scale).ceil() as u32; let height = (base_height * total_scale).ceil() as u32; ("png", Cow::Owned(pdf_to_png(pdf, width, height))) - }, + } }; let mut url = eco_format!("data:image/{format};base64,"); @@ -105,9 +106,7 @@ fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec { StandardFont::Helvetica => sf.helvetica.normal.clone(), StandardFont::HelveticaBold => sf.helvetica.bold.clone(), StandardFont::HelveticaOblique => sf.helvetica.italic.clone(), - StandardFont::HelveticaBoldOblique => { - sf.helvetica.bold_italic.clone() - } + StandardFont::HelveticaBoldOblique => sf.helvetica.bold_italic.clone(), StandardFont::Courier => sf.courier.normal.clone(), StandardFont::CourierBold => sf.courier.bold.clone(), StandardFont::CourierOblique => sf.courier.italic.clone(), @@ -121,8 +120,7 @@ fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec { }; bytes.map(|d| { - let font_data: Arc + Send + Sync> = - Arc::new(d.clone()); + let font_data: Arc + Send + Sync> = Arc::new(d.clone()); font_data }) @@ -131,9 +129,7 @@ fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec { let interpreter_settings = InterpreterSettings { font_resolver: Arc::new(move |query| match query { FontQuery::Standard(s) => select_standard_font(*s), - FontQuery::Fallback(f) => { - select_standard_font(f.pick_standard_font()) - } + FontQuery::Fallback(f) => select_standard_font(f.pick_standard_font()), }), warning_sink: Arc::new(|_| {}), }; @@ -149,4 +145,4 @@ fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec { let hayro_pix = hayro::render(page, &interpreter_settings, &render_settings); hayro_pix.take_png() -} \ No newline at end of file +}