mirror of
https://github.com/typst/typst
synced 2025-08-12 06:07:54 +08:00
Rename pdf.embed
to pdf.attach
(#6705)
This commit is contained in:
parent
1790a27de8
commit
c49b9640a6
@ -6,8 +6,8 @@ use ecow::{EcoString, eco_format};
|
||||
use rustc_hash::FxHashSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typst::foundations::{
|
||||
AutoValue, CastInfo, Func, Label, NoneValue, ParamInfo, Repr, StyleChain, Styles,
|
||||
Type, Value, fields_on, repr,
|
||||
AutoValue, CastInfo, Func, Label, NativeElement, NoneValue, ParamInfo, Repr,
|
||||
StyleChain, Styles, Type, Value, fields_on, repr,
|
||||
};
|
||||
use typst::layout::{Alignment, Dir, PagedDocument};
|
||||
use typst::syntax::ast::AstNode;
|
||||
@ -852,6 +852,7 @@ fn path_completion(func: &Func, param: &ParamInfo) -> Option<&'static [&'static
|
||||
(Some("raw"), "syntaxes") => &["sublime-syntax"],
|
||||
(Some("raw"), "theme") => &["tmtheme"],
|
||||
(Some("embed"), "path") => &[],
|
||||
(Some("attach"), "path") if *func == typst::pdf::AttachElem::ELEM => &[],
|
||||
(None, "path") => &[],
|
||||
_ => return None,
|
||||
})
|
||||
@ -1820,6 +1821,8 @@ mod tests {
|
||||
.with_source("content/a.typ", "#image()")
|
||||
.with_source("content/b.typ", "#csv(\"\")")
|
||||
.with_source("content/c.typ", "#include \"\"")
|
||||
.with_source("content/d.typ", "#pdf.attach(\"\")")
|
||||
.with_source("content/e.typ", "#math.attach(\"\")")
|
||||
.with_asset_at("assets/tiger.jpg", "tiger.jpg")
|
||||
.with_asset_at("assets/rhino.png", "rhino.png")
|
||||
.with_asset_at("data/example.csv", "example.csv");
|
||||
@ -1828,15 +1831,20 @@ mod tests {
|
||||
.must_include([q!("content/a.typ"), q!("content/b.typ"), q!("utils.typ")])
|
||||
.must_exclude([q!("assets/tiger.jpg")]);
|
||||
|
||||
test(&world, ("content/c.typ", -2))
|
||||
.must_include([q!("../main.typ"), q!("a.typ"), q!("b.typ")])
|
||||
.must_exclude([q!("c.typ")]);
|
||||
|
||||
test(&world, ("content/a.typ", -2))
|
||||
.must_include([q!("../assets/tiger.jpg"), q!("../assets/rhino.png")])
|
||||
.must_exclude([q!("../data/example.csv"), q!("b.typ")]);
|
||||
|
||||
test(&world, ("content/b.typ", -3)).must_include([q!("../data/example.csv")]);
|
||||
|
||||
test(&world, ("content/c.typ", -2))
|
||||
.must_include([q!("../main.typ"), q!("a.typ"), q!("b.typ")])
|
||||
.must_exclude([q!("c.typ")]);
|
||||
|
||||
test(&world, ("content/d.typ", -2))
|
||||
.must_include([q!("../assets/tiger.jpg"), q!("../data/example.csv")]);
|
||||
|
||||
test(&world, ("content/e.typ", -2)).must_exclude([q!("data/example.csv")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -23,7 +23,7 @@ use typst_library::model::{
|
||||
LinkElem, ListElem, Outlinable, OutlineElem, OutlineEntry, ParElem, ParbreakElem,
|
||||
QuoteElem, RefElem, StrongElem, TableCell, TableElem, TermsElem, TitleElem, Works,
|
||||
};
|
||||
use typst_library::pdf::EmbedElem;
|
||||
use typst_library::pdf::AttachElem;
|
||||
use typst_library::text::{
|
||||
DecoLine, Decoration, HighlightElem, ItalicToggle, LinebreakElem, LocalName,
|
||||
OverlineElem, RawElem, RawLine, ScriptKind, ShiftSettings, Smallcaps, SmallcapsElem,
|
||||
@ -103,7 +103,7 @@ pub fn register(rules: &mut NativeRuleMap) {
|
||||
rules.register(Paged, EQUATION_RULE);
|
||||
|
||||
// PDF.
|
||||
rules.register(Paged, EMBED_RULE);
|
||||
rules.register(Paged, ATTACH_RULE);
|
||||
}
|
||||
|
||||
const STRONG_RULE: ShowFn<StrongElem> = |elem, _, styles| {
|
||||
@ -846,4 +846,4 @@ const EQUATION_RULE: ShowFn<EquationElem> = |elem, _, styles| {
|
||||
}
|
||||
};
|
||||
|
||||
const EMBED_RULE: ShowFn<EmbedElem> = |_, _, _| Ok(Content::empty());
|
||||
const ATTACH_RULE: ShowFn<AttachElem> = |_, _, _| Ok(Content::empty());
|
||||
|
@ -446,6 +446,15 @@ impl PartialEq<&'static NativeFuncData> for Func {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Element> for Func {
|
||||
fn eq(&self, other: &Element) -> bool {
|
||||
match &self.repr {
|
||||
Repr::Element(elem) => elem == other,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Repr> for Func {
|
||||
fn from(repr: Repr) -> Self {
|
||||
Self { repr, span: Span::detached() }
|
||||
|
@ -6,18 +6,18 @@ use crate::diag::At;
|
||||
use crate::foundations::{Bytes, Cast, Derived, elem};
|
||||
use crate::introspection::Locatable;
|
||||
|
||||
/// A file that will be embedded into the output PDF.
|
||||
/// A file that will be attached to the output PDF.
|
||||
///
|
||||
/// This can be used to distribute additional files that are related to the PDF
|
||||
/// This can be used to distribute additional files associated with the PDF
|
||||
/// within it. PDF readers will display the files in a file listing.
|
||||
///
|
||||
/// Some international standards use this mechanism to embed machine-readable
|
||||
/// Some international standards use this mechanism to attach machine-readable
|
||||
/// data (e.g., ZUGFeRD/Factur-X for invoices) that mirrors the visual content
|
||||
/// of the PDF.
|
||||
///
|
||||
/// # Example
|
||||
/// ```typ
|
||||
/// #pdf.embed(
|
||||
/// #pdf.attach(
|
||||
/// "experiment.csv",
|
||||
/// relationship: "supplement",
|
||||
/// mime-type: "text/csv",
|
||||
@ -27,11 +27,11 @@ use crate::introspection::Locatable;
|
||||
///
|
||||
/// # Notes
|
||||
/// - This element is ignored if exporting to a format other than PDF.
|
||||
/// - File embeddings are not currently supported for PDF/A-2, even if the
|
||||
/// embedded file conforms to PDF/A-1 or PDF/A-2.
|
||||
#[elem(Locatable)]
|
||||
pub struct EmbedElem {
|
||||
/// The [path]($syntax/#paths) of the file to be embedded.
|
||||
/// - File attachments are not currently supported for PDF/A-2, even if the
|
||||
/// attached file conforms to PDF/A-1 or PDF/A-2.
|
||||
#[elem(keywords = ["embed"], Locatable)]
|
||||
pub struct AttachElem {
|
||||
/// The [path]($syntax/#paths) of the file to be attached.
|
||||
///
|
||||
/// Must always be specified, but is only read from if no data is provided
|
||||
/// in the following argument.
|
||||
@ -61,21 +61,21 @@ pub struct EmbedElem {
|
||||
)]
|
||||
pub data: Bytes,
|
||||
|
||||
/// The relationship of the embedded file to the document.
|
||||
/// The relationship of the attached file to the document.
|
||||
///
|
||||
/// Ignored if export doesn't target PDF/A-3.
|
||||
pub relationship: Option<EmbeddedFileRelationship>,
|
||||
pub relationship: Option<AttachedFileRelationship>,
|
||||
|
||||
/// The MIME type of the embedded file.
|
||||
/// The MIME type of the attached file.
|
||||
pub mime_type: Option<EcoString>,
|
||||
|
||||
/// A description for the embedded file.
|
||||
/// A description for the attached file.
|
||||
pub description: Option<EcoString>,
|
||||
}
|
||||
|
||||
/// The relationship of an embedded file with the document.
|
||||
/// The relationship of an attached file with the document.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum EmbeddedFileRelationship {
|
||||
pub enum AttachedFileRelationship {
|
||||
/// The PDF document was created from the source file.
|
||||
Source,
|
||||
/// The file was used to derive a visual presentation in the PDF.
|
@ -1,15 +1,21 @@
|
||||
//! PDF-specific functionality.
|
||||
|
||||
mod embed;
|
||||
mod attach;
|
||||
|
||||
pub use self::embed::*;
|
||||
pub use self::attach::*;
|
||||
|
||||
use crate::foundations::{Module, Scope};
|
||||
use crate::foundations::{Deprecation, Element, Module, Scope};
|
||||
|
||||
/// Hook up all `pdf` definitions.
|
||||
pub fn module() -> Module {
|
||||
let mut pdf = Scope::deduplicating();
|
||||
pdf.start_category(crate::Category::Pdf);
|
||||
pdf.define_elem::<EmbedElem>();
|
||||
pdf.define_elem::<AttachElem>();
|
||||
pdf.define("embed", Element::of::<AttachElem>()).deprecated(
|
||||
// Remember to remove "embed" from `path_completion` when removing this.
|
||||
Deprecation::new()
|
||||
.with_message("the name `embed` is deprecated, use `attach` instead")
|
||||
.with_until("0.15.0"),
|
||||
);
|
||||
Module::new("pdf", pdf)
|
||||
}
|
||||
|
@ -5,40 +5,40 @@ use krilla::embed::{AssociationKind, EmbeddedFile};
|
||||
use typst_library::diag::{SourceResult, bail};
|
||||
use typst_library::foundations::{NativeElement, StyleChain};
|
||||
use typst_library::layout::PagedDocument;
|
||||
use typst_library::pdf::{EmbedElem, EmbeddedFileRelationship};
|
||||
use typst_library::pdf::{AttachElem, AttachedFileRelationship};
|
||||
|
||||
pub(crate) fn embed_files(
|
||||
pub(crate) fn attach_files(
|
||||
typst_doc: &PagedDocument,
|
||||
document: &mut Document,
|
||||
) -> SourceResult<()> {
|
||||
let elements = typst_doc.introspector.query(&EmbedElem::ELEM.select());
|
||||
let elements = typst_doc.introspector.query(&AttachElem::ELEM.select());
|
||||
|
||||
for elem in &elements {
|
||||
let embed = elem.to_packed::<EmbedElem>().unwrap();
|
||||
let span = embed.span();
|
||||
let derived_path = &embed.path.derived;
|
||||
let elem = elem.to_packed::<AttachElem>().unwrap();
|
||||
let span = elem.span();
|
||||
let derived_path = &elem.path.derived;
|
||||
let path = derived_path.to_string();
|
||||
let mime_type = embed
|
||||
let mime_type = elem
|
||||
.mime_type
|
||||
.get_ref(StyleChain::default())
|
||||
.as_ref()
|
||||
.map(|s| s.to_string());
|
||||
let description = embed
|
||||
let description = elem
|
||||
.description
|
||||
.get_ref(StyleChain::default())
|
||||
.as_ref()
|
||||
.map(|s| s.to_string());
|
||||
let association_kind = match embed.relationship.get(StyleChain::default()) {
|
||||
let association_kind = match elem.relationship.get(StyleChain::default()) {
|
||||
None => AssociationKind::Unspecified,
|
||||
Some(e) => match e {
|
||||
EmbeddedFileRelationship::Source => AssociationKind::Source,
|
||||
EmbeddedFileRelationship::Data => AssociationKind::Data,
|
||||
EmbeddedFileRelationship::Alternative => AssociationKind::Alternative,
|
||||
EmbeddedFileRelationship::Supplement => AssociationKind::Supplement,
|
||||
AttachedFileRelationship::Source => AssociationKind::Source,
|
||||
AttachedFileRelationship::Data => AssociationKind::Data,
|
||||
AttachedFileRelationship::Alternative => AssociationKind::Alternative,
|
||||
AttachedFileRelationship::Supplement => AssociationKind::Supplement,
|
||||
},
|
||||
};
|
||||
let data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(embed.data.clone());
|
||||
let compress = should_compress(&embed.data);
|
||||
let data: Arc<dyn AsRef<[u8]> + Send + Sync> = Arc::new(elem.data.clone());
|
||||
let compress = should_compress(&elem.data);
|
||||
|
||||
let file = EmbeddedFile {
|
||||
path,
|
||||
@ -51,7 +51,7 @@ pub(crate) fn embed_files(
|
||||
};
|
||||
|
||||
if document.embed_file(file).is_none() {
|
||||
bail!(span, "attempted to embed file {derived_path} twice");
|
||||
bail!(span, "attempted to attach file {derived_path} twice");
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ use typst_library::visualize::{Geometry, Paint};
|
||||
use typst_syntax::Span;
|
||||
|
||||
use crate::PdfOptions;
|
||||
use crate::embed::embed_files;
|
||||
use crate::attach::attach_files;
|
||||
use crate::image::handle_image;
|
||||
use crate::link::handle_link;
|
||||
use crate::metadata::build_metadata;
|
||||
@ -63,7 +63,7 @@ pub fn convert(
|
||||
);
|
||||
|
||||
convert_pages(&mut gc, &mut document)?;
|
||||
embed_files(typst_document, &mut document)?;
|
||||
attach_files(typst_document, &mut document)?;
|
||||
|
||||
document.set_outline(build_outline(&gc));
|
||||
document.set_metadata(build_metadata(&gc));
|
||||
@ -546,19 +546,19 @@ fn convert_error(
|
||||
}
|
||||
}
|
||||
ValidationError::EmbeddedFile(e, s) => {
|
||||
// We always set the span for embedded files, so it cannot be detached.
|
||||
// We always set the span for attached files, so it cannot be detached.
|
||||
let span = to_span(*s);
|
||||
match e {
|
||||
EmbedError::Existence => {
|
||||
error!(
|
||||
span, "{prefix} document contains an embedded file";
|
||||
hint: "embedded files are not supported in this export mode"
|
||||
span, "{prefix} document contains an attached file";
|
||||
hint: "file attachments are not supported in this export mode"
|
||||
)
|
||||
}
|
||||
EmbedError::MissingDate => {
|
||||
error!(
|
||||
span, "{prefix} document date is missing";
|
||||
hint: "the document must have a date when embedding files";
|
||||
hint: "the document must have a date when attaching files";
|
||||
hint: "`set document(date: none)` must not be used in this case"
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Exporting Typst documents to PDF.
|
||||
|
||||
mod attach;
|
||||
mod convert;
|
||||
mod embed;
|
||||
mod image;
|
||||
mod link;
|
||||
mod metadata;
|
||||
|
@ -23,8 +23,8 @@ description: Changes in Typst 0.13.0
|
||||
would be displayed in italics
|
||||
- You can now specify which charset should be [covered]($text.font) by which
|
||||
font family
|
||||
- The [`pdf.embed`] function lets you embed arbitrary files in the exported
|
||||
PDF
|
||||
- The [`pdf.embed`]($pdf.attach) function lets you embed arbitrary files in the
|
||||
exported PDF
|
||||
- HTML export is currently under active development. The feature is still _very_
|
||||
incomplete, but already available for experimentation behind a feature flag.
|
||||
|
||||
@ -231,7 +231,8 @@ description: Changes in Typst 0.13.0
|
||||
- A shebang `#!` at the very start of a file is now ignored
|
||||
|
||||
## PDF export
|
||||
- Added [`pdf.embed`] function for embedding arbitrary files in the exported PDF
|
||||
- Added [`pdf.embed`]($pdf.attach) function for embedding arbitrary files in the
|
||||
exported PDF
|
||||
- Added support for PDF/A-3b export
|
||||
- The PDF timestamp will now contain the timezone by default
|
||||
|
||||
|
@ -28,9 +28,9 @@ Currently, Typst supports these PDF/A output profiles:
|
||||
|
||||
- PDF/A-3b: The basic conformance level of ISO 19005-3. This version of PDF/A is
|
||||
based on PDF 1.7 and results in archivable PDF files that can contain
|
||||
arbitrary other related files as [attachments]($pdf.embed). The only
|
||||
difference between it and PDF/A-2b is the capability to embed
|
||||
non-PDF/A-conformant files within.
|
||||
arbitrary other related files as [attachments]($pdf.attach). The only
|
||||
difference between it and PDF/A-2b is the capability to attach
|
||||
non-PDF/A-conformant files.
|
||||
|
||||
When choosing between exporting PDF/A and regular PDF, keep in mind that PDF/A
|
||||
files contain additional metadata, and that some readers will prevent the user
|
||||
|
@ -1,18 +1,18 @@
|
||||
// Test file embeddings. The tests here so far are unsatisfactory because we
|
||||
// Test file attachments. The tests here so far are unsatisfactory because we
|
||||
// have no PDF testing infrastructure. That should be improved in the future.
|
||||
|
||||
--- pdf-embed ---
|
||||
#pdf.embed("/assets/text/hello.txt")
|
||||
#pdf.embed(
|
||||
--- pdf-attach ---
|
||||
#pdf.attach("/assets/text/hello.txt")
|
||||
#pdf.attach(
|
||||
"/assets/data/details.toml",
|
||||
relationship: "supplement",
|
||||
mime-type: "application/toml",
|
||||
description: "Information about a secret project",
|
||||
)
|
||||
|
||||
--- pdf-embed-bytes ---
|
||||
#pdf.embed("hello.txt", read("/assets/text/hello.txt", encoding: none))
|
||||
#pdf.embed(
|
||||
--- pdf-attach-bytes ---
|
||||
#pdf.attach("hello.txt", read("/assets/text/hello.txt", encoding: none))
|
||||
#pdf.attach(
|
||||
"a_file_name.txt",
|
||||
read("/assets/text/hello.txt", encoding: none),
|
||||
relationship: "supplement",
|
||||
@ -20,8 +20,8 @@
|
||||
description: "A description",
|
||||
)
|
||||
|
||||
--- pdf-embed-invalid-relationship ---
|
||||
#pdf.embed(
|
||||
--- pdf-attach-invalid-relationship ---
|
||||
#pdf.attach(
|
||||
"/assets/text/hello.txt",
|
||||
// Error: 17-23 expected "source", "data", "alternative", "supplement", or none
|
||||
relationship: "test",
|
||||
@ -29,6 +29,11 @@
|
||||
description: "A test file",
|
||||
)
|
||||
|
||||
--- pdf-embed-invalid-data ---
|
||||
// Error: 38-45 expected bytes, found string
|
||||
#pdf.embed("/assets/text/hello.txt", "hello")
|
||||
--- pdf-attach-invalid-data ---
|
||||
// Error: 39-46 expected bytes, found string
|
||||
#pdf.attach("/assets/text/hello.txt", "hello")
|
||||
|
||||
--- pdf-embed-deprecated ---
|
||||
// Warning: 6-11 the name `embed` is deprecated, use `attach` instead
|
||||
// Hint: 6-11 it will be removed in Typst 0.15.0
|
||||
#pdf.embed("/assets/text/hello.txt")
|
Loading…
x
Reference in New Issue
Block a user