Compare commits

..

1 Commits

10 changed files with 110 additions and 121 deletions

17
Cargo.lock generated
View File

@ -967,7 +967,7 @@ dependencies = [
[[package]] [[package]]
name = "hayro" name = "hayro"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=d651e18#d651e188a747c188db2e206733eaf17e8f622a5d" source = "git+https://github.com/LaurenzV/hayro?rev=2b63dc8#2b63dc85b9447a815cc5b40c16338841c3780f7e"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"hayro-interpret", "hayro-interpret",
@ -980,7 +980,7 @@ dependencies = [
[[package]] [[package]]
name = "hayro-font" name = "hayro-font"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=d651e18#d651e188a747c188db2e206733eaf17e8f622a5d" source = "git+https://github.com/LaurenzV/hayro?rev=2b63dc8#2b63dc85b9447a815cc5b40c16338841c3780f7e"
dependencies = [ dependencies = [
"log", "log",
"phf", "phf",
@ -989,7 +989,7 @@ dependencies = [
[[package]] [[package]]
name = "hayro-interpret" name = "hayro-interpret"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=d651e18#d651e188a747c188db2e206733eaf17e8f622a5d" source = "git+https://github.com/LaurenzV/hayro?rev=2b63dc8#2b63dc85b9447a815cc5b40c16338841c3780f7e"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"hayro-font", "hayro-font",
@ -1006,7 +1006,7 @@ dependencies = [
[[package]] [[package]]
name = "hayro-syntax" name = "hayro-syntax"
version = "0.0.1" version = "0.0.1"
source = "git+https://github.com/LaurenzV/hayro?rev=d651e18#d651e188a747c188db2e206733eaf17e8f622a5d" source = "git+https://github.com/LaurenzV/hayro?rev=2b63dc8#2b63dc85b9447a815cc5b40c16338841c3780f7e"
dependencies = [ dependencies = [
"flate2", "flate2",
"kurbo", "kurbo",
@ -1019,7 +1019,7 @@ dependencies = [
[[package]] [[package]]
name = "hayro-write" name = "hayro-write"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=d651e18#d651e188a747c188db2e206733eaf17e8f622a5d" source = "git+https://github.com/LaurenzV/hayro?rev=2b63dc8#2b63dc85b9447a815cc5b40c16338841c3780f7e"
dependencies = [ dependencies = [
"flate2", "flate2",
"hayro-syntax", "hayro-syntax",
@ -1430,7 +1430,7 @@ dependencies = [
[[package]] [[package]]
name = "krilla" name = "krilla"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/LaurenzV/krilla/?rev=2da9d6c#2da9d6c6cb6a6baa6379592c72ae4eb7e16aa7b4" source = "git+https://github.com/LaurenzV/krilla?rev=1668ac2#1668ac2e64dc85572e6c62a2399e85acd39b619d"
dependencies = [ dependencies = [
"base64", "base64",
"bumpalo", "bumpalo",
@ -1460,7 +1460,7 @@ dependencies = [
[[package]] [[package]]
name = "krilla-svg" name = "krilla-svg"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/LaurenzV/krilla/?rev=2da9d6c#2da9d6c6cb6a6baa6379592c72ae4eb7e16aa7b4" source = "git+https://github.com/LaurenzV/krilla?rev=1668ac2#1668ac2e64dc85572e6c62a2399e85acd39b619d"
dependencies = [ dependencies = [
"flate2", "flate2",
"fontdb", "fontdb",
@ -2926,7 +2926,6 @@ dependencies = [
[[package]] [[package]]
name = "typst-assets" name = "typst-assets"
version = "0.13.1" version = "0.13.1"
source = "git+https://github.com/LaurenzV/typst-assets?rev=d89cc82#d89cc821f84a5667714491019c0b64087b2608bd"
[[package]] [[package]]
name = "typst-cli" name = "typst-cli"
@ -2976,7 +2975,7 @@ dependencies = [
[[package]] [[package]]
name = "typst-dev-assets" name = "typst-dev-assets"
version = "0.13.1" version = "0.13.1"
source = "git+https://github.com/LaurenzV/typst-dev-assets?rev=180c145#180c145cf810c8b2a7ed77355b351f3aed07d0c6" source = "git+https://github.com/typst/typst-dev-assets?rev=bfa947f#bfa947f3433d7d13a995168c40ae788a2ebfe648"
[[package]] [[package]]
name = "typst-docs" name = "typst-docs"

View File

@ -32,8 +32,8 @@ typst-svg = { path = "crates/typst-svg", version = "0.13.1" }
typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" } typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" }
typst-timing = { path = "crates/typst-timing", version = "0.13.1" } typst-timing = { path = "crates/typst-timing", version = "0.13.1" }
typst-utils = { path = "crates/typst-utils", version = "0.13.1" } typst-utils = { path = "crates/typst-utils", version = "0.13.1" }
typst-assets = { git = "https://github.com/LaurenzV/typst-assets", rev = "d89cc82" } typst-assets = { path = "../typst-assets" }
typst-dev-assets = { git = "https://github.com/LaurenzV/typst-dev-assets", rev = "180c145" } typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "bfa947f" }
arrayvec = "0.7.4" arrayvec = "0.7.4"
az = "1.2" az = "1.2"
base64 = "0.22" base64 = "0.22"
@ -61,8 +61,8 @@ fontdb = { version = "0.23", default-features = false }
fs_extra = "1.3" fs_extra = "1.3"
glidesort = "0.1.2" glidesort = "0.1.2"
hayagriva = "0.8.1" hayagriva = "0.8.1"
hayro-syntax = { git = "https://github.com/LaurenzV/hayro", rev = "d651e18" } hayro-syntax = { git = "https://github.com/LaurenzV/hayro", rev = "2b63dc8" }
hayro = { git = "https://github.com/LaurenzV/hayro", rev = "d651e18" } hayro = { git = "https://github.com/LaurenzV/hayro", rev = "2b63dc8" }
heck = "0.5" heck = "0.5"
hypher = "0.1.4" hypher = "0.1.4"
icu_properties = { version = "1.4", features = ["serde"] } icu_properties = { version = "1.4", features = ["serde"] }
@ -75,8 +75,8 @@ image = { version = "0.25.5", default-features = false, features = ["png", "jpeg
indexmap = { version = "2", features = ["serde"] } indexmap = { version = "2", features = ["serde"] }
infer = { version = "0.19.0", default-features = false } infer = { version = "0.19.0", default-features = false }
kamadak-exif = "0.6" kamadak-exif = "0.6"
krilla = { git = "https://github.com/LaurenzV/krilla/", rev = "2da9d6c", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] } krilla = { git = "https://github.com/LaurenzV/krilla", rev = "1668ac2", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] }
krilla-svg = { git = "https://github.com/LaurenzV/krilla/", rev = "2da9d6c"} krilla-svg = { git = "https://github.com/LaurenzV/krilla", rev = "1668ac2" }
kurbo = "0.11" kurbo = "0.11"
libfuzzer-sys = "0.4" libfuzzer-sys = "0.4"
lipsum = "0.9" lipsum = "0.9"

View File

@ -15,7 +15,6 @@ use std::fmt::{self, Debug, Formatter};
use std::sync::Arc; use std::sync::Arc;
use ecow::EcoString; use ecow::EcoString;
use hayro_syntax::LoadPdfError;
use typst_library::{Feature, World}; use typst_library::{Feature, World};
use typst_syntax::{Span, Spanned}; use typst_syntax::{Span, Spanned};
use typst_utils::LazyHash; use typst_utils::LazyHash;
@ -273,27 +272,9 @@ impl Packed<ImageElem> {
), ),
ImageFormat::Vector(VectorFormat::Pdf) => { ImageFormat::Vector(VectorFormat::Pdf) => {
if engine.world.library().features.is_enabled(Feature::PdfEmbedding) { if engine.world.library().features.is_enabled(Feature::PdfEmbedding) {
let document = match PdfDocument::new(loaded.data.clone()) { let document =
Ok(doc) => doc, PdfDocument::new(loaded.data.clone(), engine.world.clone())
Err(e) => match e { .within(loaded)?;
LoadPdfError::Encryption => {
bail!(
span,
"the PDF is encrypted or password-protected";
hint: "such PDFs are currently not supported";
hint: "preprocess the PDF to remove the encryption"
);
}
LoadPdfError::Invalid => {
bail!(
span,
"the PDF could not be loaded";
hint: "perhaps the PDF file is malformed"
);
}
},
};
let page_num = self.page.get(styles); let page_num = self.page.get(styles);
if page_num == 0 { if page_num == 0 {
@ -304,18 +285,16 @@ impl Packed<ImageElem> {
) )
}; };
// The user provides the page number start from 1, but further down the pipeline, // The user provides the page number start from 1, further down the pipeline,
// page numbers are 0-based. // page numbers are 0-based.
let page_idx = page_num - 1; let page_idx = page_num - 1;
let num_pages = document.len(); let num_pages = document.len();
let Some(pdf_image) = PdfImage::new(document, page_idx) else { let Some(pdf_image) = PdfImage::new(document, page_idx) else {
let pages = if num_pages == 1 { "page" } else { "pages" };
bail!( bail!(
span, span,
"page {page_num} doesn't exist"; "page {page_num} doesn't exist";
hint: "the document only has {num_pages} {pages}" hint: "the document only has {num_pages} pages"
); );
}; };

View File

@ -1,10 +1,13 @@
use crate::diag::LoadResult;
use crate::foundations::Bytes;
use crate::text::{FontStretch, FontStyle, FontVariant, FontWeight};
use crate::World;
use comemo::Tracked;
use hayro_syntax::page::Page;
use hayro_syntax::Pdf;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use typst_library::text::FontInfo;
use hayro_syntax::page::Page;
use hayro_syntax::{LoadPdfError, Pdf};
use crate::foundations::Bytes;
struct DocumentRepr { struct DocumentRepr {
pdf: Arc<Pdf>, pdf: Arc<Pdf>,
@ -26,24 +29,24 @@ impl PdfDocument {
/// Load a PDF document. /// Load a PDF document.
#[comemo::memoize] #[comemo::memoize]
#[typst_macros::time(name = "load pdf document")] #[typst_macros::time(name = "load pdf document")]
pub fn new(data: Bytes) -> Result<PdfDocument, LoadPdfError> { pub fn new(data: Bytes, world: Tracked<dyn World + '_>) -> LoadResult<PdfDocument> {
let pdf = Arc::new(Pdf::new(Arc::new(data.clone()))?); // TODO: Remove unwraps
let standard_fonts = get_standard_fonts(); let pdf = Arc::new(Pdf::new(Arc::new(data.clone())).unwrap());
let standard_fonts = get_standard_fonts(world.clone());
Ok(Self(Arc::new(DocumentRepr { data, pdf, standard_fonts }))) Ok(Self(Arc::new(DocumentRepr { data, pdf, standard_fonts })))
} }
/// Return the number of pages in the PDF.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.pdf.pages().len() self.0.pdf.pages().len()
} }
} }
struct ImageRepr { struct ImageRepr {
document: PdfDocument, pub document: PdfDocument,
page_index: usize, pub page_index: usize,
width: f32, pub width: f32,
height: f32, pub height: f32,
} }
impl Hash for ImageRepr { impl Hash for ImageRepr {
@ -53,16 +56,16 @@ impl Hash for ImageRepr {
} }
} }
/// A specific page of a PDF acting as an image. /// A page of a PDF file.
#[derive(Clone, Hash)] #[derive(Clone, Hash)]
pub struct PdfImage(Arc<ImageRepr>); pub struct PdfImage(Arc<ImageRepr>);
impl PdfImage { impl PdfImage {
/// Create a new PDF image. /// Create a new PDF image. Returns `None` if the page index is not valid.
///
/// Returns `None` if the page index is not valid.
#[comemo::memoize] #[comemo::memoize]
pub fn new(document: PdfDocument, page: usize) -> Option<PdfImage> { pub fn new(document: PdfDocument, page: usize) -> Option<PdfImage> {
// TODO: Don't allow loading if pdf-embedding feature is disabled.
// TODO: Remove Unwrap
let dimensions = document.0.pdf.pages().get(page)?.render_dimensions(); let dimensions = document.0.pdf.pages().get(page)?.render_dimensions();
Some(Self(Arc::new(ImageRepr { Some(Self(Arc::new(ImageRepr {
@ -73,93 +76,110 @@ impl PdfImage {
}))) })))
} }
/// Returns the PDF page of the image.
pub fn page(&self) -> &Page { pub fn page(&self) -> &Page {
&self.pdf().pages()[self.0.page_index] &self.0.document.0.pdf.pages()[self.0.page_index]
} }
/// Returns the underlying PDF document.
pub fn pdf(&self) -> &Arc<Pdf> { pub fn pdf(&self) -> &Arc<Pdf> {
&self.0.document.0.pdf &self.0.document.0.pdf
} }
/// Returns the width of the image.
pub fn width(&self) -> f32 { pub fn width(&self) -> f32 {
self.0.width self.0.width
} }
/// Returns the embedded standard fonts of the image.
pub fn standard_fonts(&self) -> &Arc<StandardFonts> { pub fn standard_fonts(&self) -> &Arc<StandardFonts> {
&self.0.document.0.standard_fonts &self.0.document.0.standard_fonts
} }
/// Returns the height of the image.
pub fn height(&self) -> f32 { pub fn height(&self) -> f32 {
self.0.height self.0.height
} }
/// Returns the page index of the image. pub fn data(&self) -> &Bytes {
&self.0.document.0.data
}
pub fn page_index(&self) -> usize { pub fn page_index(&self) -> usize {
self.0.page_index self.0.page_index
} }
/// Returns the underlying Typst PDF document.
pub fn document(&self) -> &PdfDocument { pub fn document(&self) -> &PdfDocument {
&self.0.document &self.0.document
} }
} }
#[comemo::memoize] #[comemo::memoize]
fn get_standard_fonts() -> Arc<StandardFonts> { fn get_standard_fonts(world: Tracked<dyn World + '_>) -> Arc<StandardFonts> {
let book = world.book();
let get_font = |name: &str, fallback_name: Option<&str>, variant: FontVariant| {
book.select(name, variant)
.or_else(|| {
if let Some(fallback_name) = fallback_name {
book.select(fallback_name, variant)
} else {
None
}
})
.or_else(|| book.select_fallback(None, variant, "A"))
.and_then(|i| world.font(i))
.map(|font| (font.data().clone(), font.index()))
};
let normal_variant = FontVariant::new(
FontStyle::Normal,
FontWeight::default(),
FontStretch::default(),
);
let bold_variant =
FontVariant::new(FontStyle::Normal, FontWeight::BOLD, FontStretch::default());
let italic_variant = FontVariant::new(
FontStyle::Italic,
FontWeight::default(),
FontStretch::default(),
);
let bold_italic_variant =
FontVariant::new(FontStyle::Italic, FontWeight::BOLD, FontStretch::default());
let helvetica = VariantFont { let helvetica = VariantFont {
normal: Bytes::new(typst_assets::pdf::SANS), normal: get_font("helvetica", Some("liberation sans"), normal_variant),
bold: Bytes::new(typst_assets::pdf::SANS_BOLD), bold: get_font("helvetica", Some("liberation sans"), bold_variant),
italic: Bytes::new(typst_assets::pdf::SANS_ITALIC), italic: get_font("helvetica", Some("liberation sans"), italic_variant),
bold_italic: Bytes::new(typst_assets::pdf::SANS_BOLD_ITALIC), bold_italic: get_font("helvetica", Some("liberation sans"), bold_italic_variant),
}; };
let courier = VariantFont { let courier = VariantFont {
normal: Bytes::new(typst_assets::pdf::FIXED), normal: get_font("courier", Some("liberation mono"), normal_variant),
bold: Bytes::new(typst_assets::pdf::FIXED_BOLD), bold: get_font("courier", Some("liberation mono"), bold_variant),
italic: Bytes::new(typst_assets::pdf::FIXED_ITALIC), italic: get_font("courier", Some("liberation mono"), italic_variant),
bold_italic: Bytes::new(typst_assets::pdf::FIXED_BOLD_ITALIC), bold_italic: get_font("courier", Some("liberation mono"), bold_italic_variant),
}; };
let times = VariantFont { let times = VariantFont {
normal: Bytes::new(typst_assets::pdf::SERIF), normal: get_font("times", Some("liberation serif"), normal_variant),
bold: Bytes::new(typst_assets::pdf::SERIF_BOLD), bold: get_font("times", Some("liberation serif"), bold_variant),
italic: Bytes::new(typst_assets::pdf::SERIF_ITALIC), italic: get_font("times", Some("liberation serif"), italic_variant),
bold_italic: Bytes::new(typst_assets::pdf::SERIF_BOLD_ITALIC), bold_italic: get_font("times", Some("liberation serif"), bold_italic_variant),
}; };
let symbol = Bytes::new(typst_assets::pdf::SYMBOL); let symbol = Some(Bytes::new(typst_assets::pdf::SYMBOL));
let zapf_dingbats = Bytes::new(typst_assets::pdf::DING_BATS); let zapf_dingbats = Some(Bytes::new(typst_assets::pdf::DING_BATS));
Arc::new(StandardFonts { helvetica, courier, times, symbol, zapf_dingbats }) Arc::new(StandardFonts { helvetica, courier, times, symbol, zapf_dingbats })
} }
/// A PDF font with multiple variants.
pub struct VariantFont { pub struct VariantFont {
/// The normal variant. pub normal: Option<(Bytes, u32)>,
pub normal: Bytes, pub bold: Option<(Bytes, u32)>,
/// The bold variant. pub italic: Option<(Bytes, u32)>,
pub bold: Bytes, pub bold_italic: Option<(Bytes, u32)>,
/// The italic variant.
pub italic: Bytes,
/// The bold-italic variant.
pub bold_italic: Bytes,
} }
/// A structure holding the raw data of all PDF standard fonts.
pub struct StandardFonts { pub struct StandardFonts {
/// The data for the `Helvetica` font family.
pub helvetica: VariantFont, pub helvetica: VariantFont,
/// The data for the `Courier` font family.
pub courier: VariantFont, pub courier: VariantFont,
/// The data for the `Times` font family.
pub times: VariantFont, pub times: VariantFont,
/// The data for the `Symbol` font family. pub symbol: Option<Bytes>,
pub symbol: Bytes, pub zapf_dingbats: Option<Bytes>,
/// The data for the `Zapf Dingbats` font family.
pub zapf_dingbats: Bytes,
} }

View File

@ -124,13 +124,15 @@ fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option<sk::Pixmap> {
StandardFont::TimesBold => sf.times.bold.clone(), StandardFont::TimesBold => sf.times.bold.clone(),
StandardFont::TimesItalic => sf.times.italic.clone(), StandardFont::TimesItalic => sf.times.italic.clone(),
StandardFont::TimesBoldItalic => sf.times.bold_italic.clone(), StandardFont::TimesBoldItalic => sf.times.bold_italic.clone(),
StandardFont::ZapfDingBats => sf.zapf_dingbats.clone(), StandardFont::ZapfDingBats => sf.zapf_dingbats.clone().map(|d| (d, 0)),
StandardFont::Symbol => sf.symbol.clone(), StandardFont::Symbol => sf.symbol.clone().map(|d| (d, 0)),
}; };
let font_data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(bytes.clone()); bytes.map(|d| {
let font_data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(d.0.clone());
Some((font_data, 0)) (font_data, d.1)
})
}; };
let interpreter_settings = InterpreterSettings { let interpreter_settings = InterpreterSettings {

View File

@ -116,13 +116,15 @@ fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec<u8> {
StandardFont::TimesBold => sf.times.bold.clone(), StandardFont::TimesBold => sf.times.bold.clone(),
StandardFont::TimesItalic => sf.times.italic.clone(), StandardFont::TimesItalic => sf.times.italic.clone(),
StandardFont::TimesBoldItalic => sf.times.bold_italic.clone(), StandardFont::TimesBoldItalic => sf.times.bold_italic.clone(),
StandardFont::ZapfDingBats => sf.zapf_dingbats.clone(), StandardFont::ZapfDingBats => sf.zapf_dingbats.clone().map(|d| (d, 0)),
StandardFont::Symbol => sf.symbol.clone(), StandardFont::Symbol => sf.symbol.clone().map(|d| (d, 0)),
}; };
let font_data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(bytes.clone()); bytes.map(|d| {
let font_data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(d.0.clone());
Some((font_data, 0)) (font_data, d.1)
})
}; };
let interpreter_settings = InterpreterSettings { let interpreter_settings = InterpreterSettings {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -198,7 +198,7 @@ fn library() -> Library {
// exactly 100pt wide. Page height is unbounded and font size is 10pt so // exactly 100pt wide. Page height is unbounded and font size is 10pt so
// that it multiplies to nice round numbers. // that it multiplies to nice round numbers.
let mut lib = Library::builder() let mut lib = Library::builder()
.with_features([Feature::Html, Feature::PdfEmbedding].into_iter().collect()) .with_features([Feature::Html].into_iter().collect())
.build(); .build();
// Hook up helpers into the global scope. // Hook up helpers into the global scope.

View File

@ -258,7 +258,7 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
--- image-png-but-pixmap-format --- --- image-png-but-pixmap-format ---
#image( #image(
read("/assets/images/tiger.jpg", encoding: none), read("/assets/images/tiger.jpg", encoding: none),
// Error: 11-18 expected "png", "jpg", "gif", "webp", dictionary, "svg", "pdf", or auto // Error: 11-18 expected "png", "jpg", "gif", "webp", dictionary, "svg", or auto
format: "rgba8", format: "rgba8",
) )
@ -289,16 +289,3 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
..rotations.map(v => raw(str(v), lang: "typc")), ..rotations.map(v => raw(str(v), lang: "typc")),
..rotations.map(rotated) ..rotations.map(rotated)
) )
--- image-pdf ---
#image("/assets/images/matplotlib.pdf")
--- image-pdf-invalid-page ---
// Error: 2-49 page 2 doesn't exist
// Hint: 2-49 the document only has 1 page
#image("/assets/images/matplotlib.pdf", page: 2)
--- image-pdf-multiple-pages ---
#image("/assets/images/diagrams.pdf", page: 1)
#image("/assets/images/diagrams.pdf", page: 3)
#image("/assets/images/diagrams.pdf", page: 2)