mirror of
https://github.com/typst/typst
synced 2025-07-27 06:17:53 +08:00
A bit of tidying
This commit is contained in:
parent
f9595e747d
commit
c80d8b07c7
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1424,7 +1424,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "krilla"
|
name = "krilla"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/LaurenzV/krilla/?rev=37b9a00#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
|
source = "git+https://github.com/LaurenzV/krilla?rev=37b9a00#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@ -1454,7 +1454,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=37b9a00#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
|
source = "git+https://github.com/LaurenzV/krilla?rev=37b9a00#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flate2",
|
"flate2",
|
||||||
"fontdb",
|
"fontdb",
|
||||||
@ -3237,6 +3237,7 @@ dependencies = [
|
|||||||
"resvg",
|
"resvg",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
"ttf-parser",
|
"ttf-parser",
|
||||||
|
"typst-assets",
|
||||||
"typst-library",
|
"typst-library",
|
||||||
"typst-macros",
|
"typst-macros",
|
||||||
"typst-timing",
|
"typst-timing",
|
||||||
@ -3253,6 +3254,7 @@ dependencies = [
|
|||||||
"hayro",
|
"hayro",
|
||||||
"image",
|
"image",
|
||||||
"ttf-parser",
|
"ttf-parser",
|
||||||
|
"typst-assets",
|
||||||
"typst-library",
|
"typst-library",
|
||||||
"typst-macros",
|
"typst-macros",
|
||||||
"typst-timing",
|
"typst-timing",
|
||||||
|
@ -74,8 +74,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 = "37b9a00", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] }
|
krilla = { git = "https://github.com/LaurenzV/krilla", rev = "37b9a00", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] }
|
||||||
krilla-svg = { git = "https://github.com/LaurenzV/krilla/", rev = "37b9a00"}
|
krilla-svg = { git = "https://github.com/LaurenzV/krilla", rev = "37b9a00"}
|
||||||
kurbo = "0.11"
|
kurbo = "0.11"
|
||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4"
|
||||||
lipsum = "0.9"
|
lipsum = "0.9"
|
||||||
|
@ -12,12 +12,13 @@ pub use self::svg::SvgImage;
|
|||||||
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use hayro_syntax::LoadPdfError;
|
use hayro_syntax::LoadPdfError;
|
||||||
use typst_syntax::{Span, Spanned};
|
use typst_syntax::{Span, Spanned};
|
||||||
use typst_utils::LazyHash;
|
use typst_utils::{LazyHash, NonZeroExt};
|
||||||
|
|
||||||
use crate::diag::{At, LoadedWithin, SourceResult, StrResult, bail, warning};
|
use crate::diag::{At, LoadedWithin, SourceResult, StrResult, bail, warning};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
@ -129,10 +130,10 @@ pub struct ImageElem {
|
|||||||
/// A text describing the image.
|
/// A text describing the image.
|
||||||
pub alt: Option<EcoString>,
|
pub alt: Option<EcoString>,
|
||||||
|
|
||||||
/// The page number that should be embedded as an image. This attribute only has an effect
|
/// The page number that should be embedded as an image. This attribute only
|
||||||
/// for PDF files.
|
/// has an effect for PDF files.
|
||||||
#[default(1)]
|
#[default(NonZeroUsize::ONE)]
|
||||||
pub page: usize,
|
pub page: NonZeroUsize,
|
||||||
|
|
||||||
/// How the image should adjust itself to a given area (the area is defined
|
/// How the image should adjust itself to a given area (the area is defined
|
||||||
/// by the `width` and `height` fields). Note that `fit` doesn't visually
|
/// by the `width` and `height` fields). Note that `fit` doesn't visually
|
||||||
@ -291,28 +292,18 @@ impl Packed<ImageElem> {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let page_num = self.page.get(styles);
|
// The user provides the page number start from 1, but further
|
||||||
|
// down the pipeline, page numbers are 0-based.
|
||||||
if page_num == 0 {
|
let page_num = self.page.get(styles).get();
|
||||||
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, but further down the pipeline,
|
|
||||||
// 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.num_pages();
|
||||||
|
|
||||||
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" };
|
let s = if num_pages == 1 { "" } else { "s" };
|
||||||
|
|
||||||
bail!(
|
bail!(
|
||||||
span,
|
span,
|
||||||
"page {page_num} doesn't exist";
|
"page {page_num} does not exist";
|
||||||
hint: "the document only has {num_pages} {pages}"
|
hint: "the document only has {num_pages} page{s}"
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -567,7 +558,8 @@ fn is_svg(data: &[u8]) -> bool {
|
|||||||
pub enum VectorFormat {
|
pub enum VectorFormat {
|
||||||
/// The vector graphics format of the web.
|
/// The vector graphics format of the web.
|
||||||
Svg,
|
Svg,
|
||||||
/// The PDF graphics format.
|
/// High-fidelity document and graphics format, with focus on exact
|
||||||
|
/// reproduction in print.
|
||||||
Pdf,
|
Pdf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,34 @@ use hayro_syntax::{LoadPdfError, Pdf};
|
|||||||
|
|
||||||
use crate::foundations::Bytes;
|
use crate::foundations::Bytes;
|
||||||
|
|
||||||
|
/// A PDF document.
|
||||||
|
#[derive(Clone, Hash)]
|
||||||
|
pub struct PdfDocument(Arc<DocumentRepr>);
|
||||||
|
|
||||||
|
/// The internal representation of a `PdfDocument`.
|
||||||
struct DocumentRepr {
|
struct DocumentRepr {
|
||||||
pdf: Arc<Pdf>,
|
pdf: Arc<Pdf>,
|
||||||
data: Bytes,
|
data: Bytes,
|
||||||
standard_fonts: Arc<StandardFonts>,
|
}
|
||||||
|
|
||||||
|
impl PdfDocument {
|
||||||
|
/// Loads a PDF document.
|
||||||
|
#[comemo::memoize]
|
||||||
|
#[typst_macros::time(name = "load pdf document")]
|
||||||
|
pub fn new(data: Bytes) -> Result<PdfDocument, LoadPdfError> {
|
||||||
|
let pdf = Arc::new(Pdf::new(Arc::new(data.clone()))?);
|
||||||
|
Ok(Self(Arc::new(DocumentRepr { data, pdf })))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the underlying PDF document.
|
||||||
|
pub fn pdf(&self) -> &Arc<Pdf> {
|
||||||
|
&self.0.pdf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of pages in the PDF.
|
||||||
|
pub fn num_pages(&self) -> usize {
|
||||||
|
self.0.pdf.pages().len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for DocumentRepr {
|
impl Hash for DocumentRepr {
|
||||||
@ -18,27 +42,11 @@ impl Hash for DocumentRepr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A PDF document.
|
/// A specific page of a PDF acting as an image.
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct PdfDocument(Arc<DocumentRepr>);
|
pub struct PdfImage(Arc<ImageRepr>);
|
||||||
|
|
||||||
impl PdfDocument {
|
|
||||||
/// Load a PDF document.
|
|
||||||
#[comemo::memoize]
|
|
||||||
#[typst_macros::time(name = "load pdf document")]
|
|
||||||
pub fn new(data: Bytes) -> Result<PdfDocument, LoadPdfError> {
|
|
||||||
let pdf = Arc::new(Pdf::new(Arc::new(data.clone()))?);
|
|
||||||
let standard_fonts = get_standard_fonts();
|
|
||||||
|
|
||||||
Ok(Self(Arc::new(DocumentRepr { data, pdf, standard_fonts })))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the number of pages in the PDF.
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.0.pdf.pages().len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// The internal representation of a `PdfImage`.
|
||||||
struct ImageRepr {
|
struct ImageRepr {
|
||||||
document: PdfDocument,
|
document: PdfDocument,
|
||||||
page_index: usize,
|
page_index: usize,
|
||||||
@ -46,41 +54,24 @@ struct ImageRepr {
|
|||||||
height: f32,
|
height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for ImageRepr {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.document.hash(state);
|
|
||||||
self.page_index.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A specific page of a PDF acting as an image.
|
|
||||||
#[derive(Clone, Hash)]
|
|
||||||
pub struct PdfImage(Arc<ImageRepr>);
|
|
||||||
|
|
||||||
impl PdfImage {
|
impl PdfImage {
|
||||||
/// Create a new PDF image.
|
/// Creates 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_index: usize) -> Option<PdfImage> {
|
||||||
let dimensions = document.0.pdf.pages().get(page)?.render_dimensions();
|
let (width, height) = document.0.pdf.pages().get(page_index)?.render_dimensions();
|
||||||
|
Some(Self(Arc::new(ImageRepr { document, page_index, width, height })))
|
||||||
|
}
|
||||||
|
|
||||||
Some(Self(Arc::new(ImageRepr {
|
/// Returns the underlying Typst PDF document.
|
||||||
document,
|
pub fn document(&self) -> &PdfDocument {
|
||||||
page_index: page,
|
&self.0.document
|
||||||
width: dimensions.0,
|
|
||||||
height: dimensions.1,
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the PDF page of the image.
|
/// 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.document().pdf().pages()[self.0.page_index]
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the underlying PDF document.
|
|
||||||
pub fn pdf(&self) -> &Arc<Pdf> {
|
|
||||||
&self.0.document.0.pdf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the width of the image.
|
/// Returns the width of the image.
|
||||||
@ -88,11 +79,6 @@ impl PdfImage {
|
|||||||
self.0.width
|
self.0.width
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the embedded standard fonts of the image.
|
|
||||||
pub fn standard_fonts(&self) -> &Arc<StandardFonts> {
|
|
||||||
&self.0.document.0.standard_fonts
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the height of the image.
|
/// Returns the height of the image.
|
||||||
pub fn height(&self) -> f32 {
|
pub fn height(&self) -> f32 {
|
||||||
self.0.height
|
self.0.height
|
||||||
@ -102,64 +88,11 @@ impl PdfImage {
|
|||||||
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 {
|
|
||||||
&self.0.document
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[comemo::memoize]
|
impl Hash for ImageRepr {
|
||||||
fn get_standard_fonts() -> Arc<StandardFonts> {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
let helvetica = VariantFont {
|
self.document.hash(state);
|
||||||
normal: Bytes::new(typst_assets::pdf::SANS),
|
self.page_index.hash(state);
|
||||||
bold: Bytes::new(typst_assets::pdf::SANS_BOLD),
|
|
||||||
italic: Bytes::new(typst_assets::pdf::SANS_ITALIC),
|
|
||||||
bold_italic: Bytes::new(typst_assets::pdf::SANS_BOLD_ITALIC),
|
|
||||||
};
|
|
||||||
|
|
||||||
let courier = VariantFont {
|
|
||||||
normal: Bytes::new(typst_assets::pdf::FIXED),
|
|
||||||
bold: Bytes::new(typst_assets::pdf::FIXED_BOLD),
|
|
||||||
italic: Bytes::new(typst_assets::pdf::FIXED_ITALIC),
|
|
||||||
bold_italic: Bytes::new(typst_assets::pdf::FIXED_BOLD_ITALIC),
|
|
||||||
};
|
|
||||||
|
|
||||||
let times = VariantFont {
|
|
||||||
normal: Bytes::new(typst_assets::pdf::SERIF),
|
|
||||||
bold: Bytes::new(typst_assets::pdf::SERIF_BOLD),
|
|
||||||
italic: Bytes::new(typst_assets::pdf::SERIF_ITALIC),
|
|
||||||
bold_italic: Bytes::new(typst_assets::pdf::SERIF_BOLD_ITALIC),
|
|
||||||
};
|
|
||||||
|
|
||||||
let symbol = Bytes::new(typst_assets::pdf::SYMBOL);
|
|
||||||
let zapf_dingbats = Bytes::new(typst_assets::pdf::DING_BATS);
|
|
||||||
|
|
||||||
Arc::new(StandardFonts { helvetica, courier, times, symbol, zapf_dingbats })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A PDF font with multiple variants.
|
|
||||||
pub struct VariantFont {
|
|
||||||
/// The normal variant.
|
|
||||||
pub normal: Bytes,
|
|
||||||
/// The bold variant.
|
|
||||||
pub bold: Bytes,
|
|
||||||
/// 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 {
|
|
||||||
/// The data for the `Helvetica` font family.
|
|
||||||
pub helvetica: VariantFont,
|
|
||||||
/// The data for the `Courier` font family.
|
|
||||||
pub courier: VariantFont,
|
|
||||||
/// The data for the `Times` font family.
|
|
||||||
pub times: VariantFont,
|
|
||||||
/// The data for the `Symbol` font family.
|
|
||||||
pub symbol: Bytes,
|
|
||||||
/// The data for the `Zapf Dingbats` font family.
|
|
||||||
pub zapf_dingbats: Bytes,
|
|
||||||
}
|
}
|
||||||
|
@ -368,17 +368,20 @@ fn finish(
|
|||||||
let span = to_span(loc);
|
let span = to_span(loc);
|
||||||
match e {
|
match e {
|
||||||
// We already validated in `typst-library` that the page index is valid.
|
// We already validated in `typst-library` that the page index is valid.
|
||||||
PdfError::InvalidPage(_) => unreachable!(),
|
PdfError::InvalidPage(_) => bail!(
|
||||||
|
span,
|
||||||
|
"invalid page number for PDF file";
|
||||||
|
hint: "please report this as a bug"
|
||||||
|
),
|
||||||
PdfError::VersionMismatch(v) => {
|
PdfError::VersionMismatch(v) => {
|
||||||
let pdf_ver = v.as_str();
|
let pdf_ver = v.as_str();
|
||||||
let config_ver = configuration.version();
|
let config_ver = configuration.version();
|
||||||
let cur_ver = config_ver.as_str();
|
let cur_ver = config_ver.as_str();
|
||||||
|
|
||||||
bail!(span,
|
bail!(span,
|
||||||
"the version of the PDF file is too high";
|
"the version of the PDF is too high";
|
||||||
hint: "the current export target is {cur_ver}, while the PDF has version {pdf_ver}";
|
hint: "the current export target is {cur_ver}, while the PDF has version {pdf_ver}";
|
||||||
hint: "raise the export target to {pdf_ver} or higher";
|
hint: "raise the export target to {pdf_ver} or higher";
|
||||||
hint: "preprocess the PDF to convert it to a lower version"
|
hint: "or preprocess the PDF to convert it to a lower version"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -387,14 +390,14 @@ fn finish(
|
|||||||
let span = to_span(loc);
|
let span = to_span(loc);
|
||||||
bail!(span,
|
bail!(span,
|
||||||
"duplicate tag id";
|
"duplicate tag id";
|
||||||
hint: "this is a bug in typst, please report it"
|
hint: "please report this as a bug"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
KrillaError::UnknownTagId(_, loc) => {
|
KrillaError::UnknownTagId(_, loc) => {
|
||||||
let span = to_span(loc);
|
let span = to_span(loc);
|
||||||
bail!(span,
|
bail!(span,
|
||||||
"unknown tag id";
|
"unknown tag id";
|
||||||
hint: "this is a bug in typst, please report it"
|
hint: "please report this as a bug"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -614,7 +617,7 @@ fn convert_error(
|
|||||||
error!(
|
error!(
|
||||||
to_span(*loc),
|
to_span(*loc),
|
||||||
"embedding PDFs is currently not supported in this export mode";
|
"embedding PDFs is currently not supported in this export mode";
|
||||||
hint: "try converting the PDF to SVG before embedding it"
|
hint: "try converting the PDF to an SVG before embedding it"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ fn convert_raster(
|
|||||||
|
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
fn convert_pdf(pdf: &PdfImage) -> PdfDocument {
|
fn convert_pdf(pdf: &PdfImage) -> PdfDocument {
|
||||||
PdfDocument::new(pdf.pdf().clone())
|
PdfDocument::new(pdf.document().pdf().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exif_transform(image: &RasterImage, size: Size) -> (Transform, Size) {
|
fn exif_transform(image: &RasterImage, size: Size) -> (Transform, Size) {
|
||||||
|
@ -13,6 +13,7 @@ keywords = { workspace = true }
|
|||||||
readme = { workspace = true }
|
readme = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
typst-assets = { workspace = true }
|
||||||
typst-library = { workspace = true }
|
typst-library = { workspace = true }
|
||||||
typst-macros = { workspace = true }
|
typst-macros = { workspace = true }
|
||||||
typst-timing = { workspace = true }
|
typst-timing = { workspace = true }
|
||||||
|
@ -97,7 +97,6 @@ fn build_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
|
|||||||
h as f32 / tree.size().height(),
|
h as f32 / tree.size().height(),
|
||||||
);
|
);
|
||||||
resvg::render(tree, ts, &mut texture.as_mut());
|
resvg::render(tree, ts, &mut texture.as_mut());
|
||||||
|
|
||||||
texture
|
texture
|
||||||
}
|
}
|
||||||
ImageKind::Pdf(pdf) => build_pdf_texture(pdf, w, h)?,
|
ImageKind::Pdf(pdf) => build_pdf_texture(pdf, w, h)?,
|
||||||
@ -108,29 +107,24 @@ fn build_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
|
|||||||
|
|
||||||
// Keep this in sync with `typst-svg`!
|
// Keep this in sync with `typst-svg`!
|
||||||
fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option<sk::Pixmap> {
|
fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option<sk::Pixmap> {
|
||||||
let sf = pdf.standard_fonts().clone();
|
|
||||||
|
|
||||||
let select_standard_font = move |font: StandardFont| -> Option<(FontData, u32)> {
|
let select_standard_font = move |font: StandardFont| -> Option<(FontData, u32)> {
|
||||||
let bytes = match font {
|
let bytes = match font {
|
||||||
StandardFont::Helvetica => sf.helvetica.normal.clone(),
|
StandardFont::Helvetica => typst_assets::pdf::SANS,
|
||||||
StandardFont::HelveticaBold => sf.helvetica.bold.clone(),
|
StandardFont::HelveticaBold => typst_assets::pdf::SANS_BOLD,
|
||||||
StandardFont::HelveticaOblique => sf.helvetica.italic.clone(),
|
StandardFont::HelveticaOblique => typst_assets::pdf::SANS_ITALIC,
|
||||||
StandardFont::HelveticaBoldOblique => sf.helvetica.bold_italic.clone(),
|
StandardFont::HelveticaBoldOblique => typst_assets::pdf::SANS_BOLD_ITALIC,
|
||||||
StandardFont::Courier => sf.courier.normal.clone(),
|
StandardFont::Courier => typst_assets::pdf::FIXED,
|
||||||
StandardFont::CourierBold => sf.courier.bold.clone(),
|
StandardFont::CourierBold => typst_assets::pdf::FIXED_BOLD,
|
||||||
StandardFont::CourierOblique => sf.courier.italic.clone(),
|
StandardFont::CourierOblique => typst_assets::pdf::FIXED_ITALIC,
|
||||||
StandardFont::CourierBoldOblique => sf.courier.bold_italic.clone(),
|
StandardFont::CourierBoldOblique => typst_assets::pdf::FIXED_BOLD_ITALIC,
|
||||||
StandardFont::TimesRoman => sf.times.normal.clone(),
|
StandardFont::TimesRoman => typst_assets::pdf::SERIF,
|
||||||
StandardFont::TimesBold => sf.times.bold.clone(),
|
StandardFont::TimesBold => typst_assets::pdf::SERIF_BOLD,
|
||||||
StandardFont::TimesItalic => sf.times.italic.clone(),
|
StandardFont::TimesItalic => typst_assets::pdf::SERIF_ITALIC,
|
||||||
StandardFont::TimesBoldItalic => sf.times.bold_italic.clone(),
|
StandardFont::TimesBoldItalic => typst_assets::pdf::SERIF_BOLD_ITALIC,
|
||||||
StandardFont::ZapfDingBats => sf.zapf_dingbats.clone(),
|
StandardFont::ZapfDingBats => typst_assets::pdf::DING_BATS,
|
||||||
StandardFont::Symbol => sf.symbol.clone(),
|
StandardFont::Symbol => typst_assets::pdf::SYMBOL,
|
||||||
};
|
};
|
||||||
|
Some((Arc::new(bytes), 0))
|
||||||
let font_data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(bytes.clone());
|
|
||||||
|
|
||||||
Some((font_data, 0))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let interpreter_settings = InterpreterSettings {
|
let interpreter_settings = InterpreterSettings {
|
||||||
@ -140,7 +134,6 @@ fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option<sk::Pixmap> {
|
|||||||
}),
|
}),
|
||||||
warning_sink: Arc::new(|_| {}),
|
warning_sink: Arc::new(|_| {}),
|
||||||
};
|
};
|
||||||
let page = pdf.page();
|
|
||||||
|
|
||||||
let render_settings = RenderSettings {
|
let render_settings = RenderSettings {
|
||||||
x_scale: w as f32 / pdf.width(),
|
x_scale: w as f32 / pdf.width(),
|
||||||
@ -149,7 +142,7 @@ fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option<sk::Pixmap> {
|
|||||||
height: Some(h as u16),
|
height: Some(h as u16),
|
||||||
};
|
};
|
||||||
|
|
||||||
let hayro_pix = hayro::render(page, &interpreter_settings, &render_settings);
|
let hayro_pix = hayro::render(pdf.page(), &interpreter_settings, &render_settings);
|
||||||
|
|
||||||
sk::Pixmap::from_vec(hayro_pix.take_u8(), IntSize::from_wh(w, h)?)
|
sk::Pixmap::from_vec(hayro_pix.take_u8(), IntSize::from_wh(w, h)?)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ keywords = { workspace = true }
|
|||||||
readme = { workspace = true }
|
readme = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
typst-assets = { workspace = true }
|
||||||
typst-library = { workspace = true }
|
typst-library = { workspace = true }
|
||||||
typst-macros = { workspace = true }
|
typst-macros = { workspace = true }
|
||||||
typst-timing = { workspace = true }
|
typst-timing = { workspace = true }
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
@ -48,7 +47,7 @@ pub fn convert_image_scaling(scaling: Smart<ImageScaling>) -> Option<&'static st
|
|||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
||||||
let mut buf;
|
let mut buf;
|
||||||
let (format, data): (&str, Cow<[u8]>) = match image.kind() {
|
let (format, data): (&str, &[u8]) = match image.kind() {
|
||||||
ImageKind::Raster(raster) => match raster.format() {
|
ImageKind::Raster(raster) => match raster.format() {
|
||||||
RasterFormat::Exchange(format) => (
|
RasterFormat::Exchange(format) => (
|
||||||
match format {
|
match format {
|
||||||
@ -57,7 +56,7 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
|||||||
ExchangeFormat::Gif => "gif",
|
ExchangeFormat::Gif => "gif",
|
||||||
ExchangeFormat::Webp => "webp",
|
ExchangeFormat::Webp => "webp",
|
||||||
},
|
},
|
||||||
Cow::Borrowed(raster.data()),
|
raster.data(),
|
||||||
),
|
),
|
||||||
RasterFormat::Pixel(_) => ("png", {
|
RasterFormat::Pixel(_) => ("png", {
|
||||||
buf = vec![];
|
buf = vec![];
|
||||||
@ -66,14 +65,15 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
|||||||
encoder.set_icc_profile(icc_profile.to_vec()).ok();
|
encoder.set_icc_profile(icc_profile.to_vec()).ok();
|
||||||
}
|
}
|
||||||
raster.dynamic().write_with_encoder(encoder).unwrap();
|
raster.dynamic().write_with_encoder(encoder).unwrap();
|
||||||
Cow::Borrowed(buf.as_slice())
|
buf.as_slice()
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
ImageKind::Svg(svg) => ("svg+xml", Cow::Borrowed(svg.data())),
|
ImageKind::Svg(svg) => ("svg+xml", svg.data()),
|
||||||
ImageKind::Pdf(pdf) => {
|
ImageKind::Pdf(pdf) => {
|
||||||
// To make sure the image isn't pixelated, we always scale up so the lowest
|
// To make sure the image isn't pixelated, we always scale up so the
|
||||||
// dimension has at least 1000 pixels. However, we only scale up as much so that the
|
// lowest dimension has at least 1000 pixels. However, we only scale
|
||||||
// largest dimension doesn't exceed 3000 pixels.
|
// up as much so that the largest dimension doesn't exceed 3000
|
||||||
|
// pixels.
|
||||||
const MIN_RES: f32 = 1000.0;
|
const MIN_RES: f32 = 1000.0;
|
||||||
const MAX_RES: f32 = 3000.0;
|
const MAX_RES: f32 = 3000.0;
|
||||||
|
|
||||||
@ -81,13 +81,12 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
|||||||
let w_scale = (MIN_RES / base_width).max(MAX_RES / base_width);
|
let w_scale = (MIN_RES / base_width).max(MAX_RES / base_width);
|
||||||
let base_height = pdf.height();
|
let base_height = pdf.height();
|
||||||
let h_scale = (MIN_RES / base_height).min(MAX_RES / base_height);
|
let h_scale = (MIN_RES / base_height).min(MAX_RES / base_height);
|
||||||
|
|
||||||
let total_scale = w_scale.min(h_scale);
|
let total_scale = w_scale.min(h_scale);
|
||||||
|
|
||||||
let width = (base_width * total_scale).ceil() as u32;
|
let width = (base_width * total_scale).ceil() as u32;
|
||||||
let height = (base_height * total_scale).ceil() as u32;
|
let height = (base_height * total_scale).ceil() as u32;
|
||||||
|
|
||||||
("png", Cow::Owned(pdf_to_png(pdf, width, height)))
|
buf = pdf_to_png(pdf, width, height);
|
||||||
|
("png", buf.as_slice())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,31 +97,25 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Keep this in sync with `typst-png`!
|
// Keep this in sync with `typst-png`!
|
||||||
#[comemo::memoize]
|
|
||||||
fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec<u8> {
|
fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec<u8> {
|
||||||
let sf = pdf.standard_fonts().clone();
|
|
||||||
|
|
||||||
let select_standard_font = move |font: StandardFont| -> Option<(FontData, u32)> {
|
let select_standard_font = move |font: StandardFont| -> Option<(FontData, u32)> {
|
||||||
let bytes = match font {
|
let bytes = match font {
|
||||||
StandardFont::Helvetica => sf.helvetica.normal.clone(),
|
StandardFont::Helvetica => typst_assets::pdf::SANS,
|
||||||
StandardFont::HelveticaBold => sf.helvetica.bold.clone(),
|
StandardFont::HelveticaBold => typst_assets::pdf::SANS_BOLD,
|
||||||
StandardFont::HelveticaOblique => sf.helvetica.italic.clone(),
|
StandardFont::HelveticaOblique => typst_assets::pdf::SANS_ITALIC,
|
||||||
StandardFont::HelveticaBoldOblique => sf.helvetica.bold_italic.clone(),
|
StandardFont::HelveticaBoldOblique => typst_assets::pdf::SANS_BOLD_ITALIC,
|
||||||
StandardFont::Courier => sf.courier.normal.clone(),
|
StandardFont::Courier => typst_assets::pdf::FIXED,
|
||||||
StandardFont::CourierBold => sf.courier.bold.clone(),
|
StandardFont::CourierBold => typst_assets::pdf::FIXED_BOLD,
|
||||||
StandardFont::CourierOblique => sf.courier.italic.clone(),
|
StandardFont::CourierOblique => typst_assets::pdf::FIXED_ITALIC,
|
||||||
StandardFont::CourierBoldOblique => sf.courier.bold_italic.clone(),
|
StandardFont::CourierBoldOblique => typst_assets::pdf::FIXED_BOLD_ITALIC,
|
||||||
StandardFont::TimesRoman => sf.times.normal.clone(),
|
StandardFont::TimesRoman => typst_assets::pdf::SERIF,
|
||||||
StandardFont::TimesBold => sf.times.bold.clone(),
|
StandardFont::TimesBold => typst_assets::pdf::SERIF_BOLD,
|
||||||
StandardFont::TimesItalic => sf.times.italic.clone(),
|
StandardFont::TimesItalic => typst_assets::pdf::SERIF_ITALIC,
|
||||||
StandardFont::TimesBoldItalic => sf.times.bold_italic.clone(),
|
StandardFont::TimesBoldItalic => typst_assets::pdf::SERIF_BOLD_ITALIC,
|
||||||
StandardFont::ZapfDingBats => sf.zapf_dingbats.clone(),
|
StandardFont::ZapfDingBats => typst_assets::pdf::DING_BATS,
|
||||||
StandardFont::Symbol => sf.symbol.clone(),
|
StandardFont::Symbol => typst_assets::pdf::SYMBOL,
|
||||||
};
|
};
|
||||||
|
Some((Arc::new(bytes), 0))
|
||||||
let font_data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(bytes.clone());
|
|
||||||
|
|
||||||
Some((font_data, 0))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let interpreter_settings = InterpreterSettings {
|
let interpreter_settings = InterpreterSettings {
|
||||||
@ -132,7 +125,6 @@ fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec<u8> {
|
|||||||
}),
|
}),
|
||||||
warning_sink: Arc::new(|_| {}),
|
warning_sink: Arc::new(|_| {}),
|
||||||
};
|
};
|
||||||
let page = pdf.page();
|
|
||||||
|
|
||||||
let render_settings = RenderSettings {
|
let render_settings = RenderSettings {
|
||||||
x_scale: w as f32 / pdf.width(),
|
x_scale: w as f32 / pdf.width(),
|
||||||
@ -141,7 +133,7 @@ fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec<u8> {
|
|||||||
height: Some(h as u16),
|
height: Some(h as u16),
|
||||||
};
|
};
|
||||||
|
|
||||||
let hayro_pix = hayro::render(page, &interpreter_settings, &render_settings);
|
let hayro_pix = hayro::render(pdf.page(), &interpreter_settings, &render_settings);
|
||||||
|
|
||||||
hayro_pix.take_png()
|
hayro_pix.take_png()
|
||||||
}
|
}
|
||||||
|
@ -293,12 +293,12 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
|
|||||||
--- image-pdf ---
|
--- image-pdf ---
|
||||||
#image("/assets/images/matplotlib.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-pdf-multiple-pages ---
|
||||||
#image("/assets/images/diagrams.pdf", page: 1)
|
#image("/assets/images/diagrams.pdf", page: 1)
|
||||||
#image("/assets/images/diagrams.pdf", page: 3)
|
#image("/assets/images/diagrams.pdf", page: 3)
|
||||||
#image("/assets/images/diagrams.pdf", page: 2)
|
#image("/assets/images/diagrams.pdf", page: 2)
|
||||||
|
|
||||||
|
--- image-pdf-invalid-page ---
|
||||||
|
// Error: 2-49 page 2 does not exist
|
||||||
|
// Hint: 2-49 the document only has 1 page
|
||||||
|
#image("/assets/images/matplotlib.pdf", page: 2)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user