mirror of
https://github.com/typst/typst
synced 2025-05-21 20:45:27 +08:00
106 lines
3.4 KiB
Rust
106 lines
3.4 KiB
Rust
use ecow::EcoString;
|
|
use typst_library::foundations::Target;
|
|
use typst_syntax::Spanned;
|
|
|
|
use crate::diag::{warning, At, SourceResult};
|
|
use crate::engine::Engine;
|
|
use crate::foundations::{
|
|
elem, Bytes, Cast, Content, Derived, Packed, Show, StyleChain, TargetElem,
|
|
};
|
|
use crate::introspection::Locatable;
|
|
use crate::World;
|
|
|
|
/// A file that will be embedded into the output PDF.
|
|
///
|
|
/// This can be used to distribute additional files that are related to the PDF
|
|
/// within it. PDF readers will display the files in a file listing.
|
|
///
|
|
/// Some international standards use this mechanism to embed machine-readable
|
|
/// data (e.g., ZUGFeRD/Factur-X for invoices) that mirrors the visual content
|
|
/// of the PDF.
|
|
///
|
|
/// # Example
|
|
/// ```typ
|
|
/// #pdf.embed(
|
|
/// "experiment.csv",
|
|
/// relationship: "supplement",
|
|
/// mime-type: "text/csv",
|
|
/// description: "Raw Oxygen readings from the Arctic experiment",
|
|
/// )
|
|
/// ```
|
|
///
|
|
/// # 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(Show, Locatable)]
|
|
pub struct EmbedElem {
|
|
/// The [path]($syntax/#paths) of the file to be embedded.
|
|
///
|
|
/// Must always be specified, but is only read from if no data is provided
|
|
/// in the following argument.
|
|
#[required]
|
|
#[parse(
|
|
let Spanned { v: path, span } =
|
|
args.expect::<Spanned<EcoString>>("path")?;
|
|
let id = span.resolve_path(&path).at(span)?;
|
|
// The derived part is the project-relative resolved path.
|
|
let resolved = id.vpath().as_rootless_path().to_string_lossy().replace("\\", "/").into();
|
|
Derived::new(path.clone(), resolved)
|
|
)]
|
|
#[borrowed]
|
|
pub path: Derived<EcoString, EcoString>,
|
|
|
|
/// Raw file data, optionally.
|
|
///
|
|
/// If omitted, the data is read from the specified path.
|
|
#[positional]
|
|
// Not actually required as an argument, but always present as a field.
|
|
// We can't distinguish between the two at the moment.
|
|
#[required]
|
|
#[parse(
|
|
match args.find::<Bytes>()? {
|
|
Some(data) => data,
|
|
None => engine.world.file(id).at(span)?,
|
|
}
|
|
)]
|
|
pub data: Bytes,
|
|
|
|
/// The relationship of the embedded file to the document.
|
|
///
|
|
/// Ignored if export doesn't target PDF/A-3.
|
|
pub relationship: Option<EmbeddedFileRelationship>,
|
|
|
|
/// The MIME type of the embedded file.
|
|
#[borrowed]
|
|
pub mime_type: Option<EcoString>,
|
|
|
|
/// A description for the embedded file.
|
|
#[borrowed]
|
|
pub description: Option<EcoString>,
|
|
}
|
|
|
|
impl Show for Packed<EmbedElem> {
|
|
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
|
if TargetElem::target_in(styles) == Target::Html {
|
|
engine
|
|
.sink
|
|
.warn(warning!(self.span(), "embed was ignored during HTML export"));
|
|
}
|
|
Ok(Content::empty())
|
|
}
|
|
}
|
|
|
|
/// The relationship of an embedded file with the document.
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
|
pub enum EmbeddedFileRelationship {
|
|
/// The PDF document was created from the source file.
|
|
Source,
|
|
/// The file was used to derive a visual presentation in the PDF.
|
|
Data,
|
|
/// An alternative representation of the document.
|
|
Alternative,
|
|
/// Additional resources for the document.
|
|
Supplement,
|
|
}
|