summaryrefslogtreecommitdiff
path: root/crates/typst-pdf/src/resources.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-pdf/src/resources.rs')
-rw-r--r--crates/typst-pdf/src/resources.rs349
1 files changed, 0 insertions, 349 deletions
diff --git a/crates/typst-pdf/src/resources.rs b/crates/typst-pdf/src/resources.rs
deleted file mode 100644
index bdbf2f1e..00000000
--- a/crates/typst-pdf/src/resources.rs
+++ /dev/null
@@ -1,349 +0,0 @@
-//! PDF resources.
-//!
-//! Resources are defined in dictionaries. They map identifiers such as `Im0` to
-//! a PDF reference. Each [content stream] is associated with a resource dictionary.
-//! The identifiers defined in the resources can then be used in content streams.
-//!
-//! [content stream]: `crate::content`
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-use ecow::{eco_format, EcoString};
-use pdf_writer::{Dict, Finish, Name, Ref};
-use subsetter::GlyphRemapper;
-use typst_library::diag::{SourceResult, StrResult};
-use typst_library::text::{Font, Lang};
-use typst_library::visualize::Image;
-use typst_syntax::Span;
-use typst_utils::Deferred;
-
-use crate::color::ColorSpaces;
-use crate::color_font::ColorFontMap;
-use crate::extg::ExtGState;
-use crate::gradient::PdfGradient;
-use crate::image::EncodedImage;
-use crate::tiling::TilingRemapper;
-use crate::{PdfChunk, Renumber, WithEverything, WithResources};
-
-/// All the resources that have been collected when traversing the document.
-///
-/// This does not allocate references to resources, only track what was used
-/// and deduplicate what can be deduplicated.
-///
-/// You may notice that this structure is a tree: [`TilingRemapper`] and
-/// [`ColorFontMap`] (that are present in the fields of [`Resources`]),
-/// themselves contain [`Resources`] (that will be called "sub-resources" from
-/// now on). Because color glyphs and tilings are defined using content
-/// streams, just like pages, they can refer to resources too, which are tracked
-/// by the respective sub-resources.
-///
-/// Each instance of this structure will become a `/Resources` dictionary in
-/// the final PDF. It is not possible to use a single shared dictionary for all
-/// pages, tilings and color fonts, because if a resource is listed in its own
-/// `/Resources` dictionary, some PDF readers will fail to open the document.
-///
-/// Because we need to lazily initialize sub-resources (we don't know how deep
-/// the tree will be before reading the document), and that this is done in a
-/// context where no PDF reference allocator is available, `Resources` are
-/// originally created with the type parameter `R = ()`. The reference for each
-/// dictionary will only be allocated in the next phase, once we know the shape
-/// of the tree, at which point `R` becomes `Ref`. No other value of `R` should
-/// ever exist.
-pub struct Resources<R = Ref> {
- /// The global reference to this resource dictionary, or `()` if it has not
- /// been allocated yet.
- pub reference: R,
-
- /// Handles color space writing.
- pub colors: ColorSpaces,
-
- /// Deduplicates fonts used across the document.
- pub fonts: Remapper<Font>,
- /// Deduplicates images used across the document.
- pub images: Remapper<Image>,
- /// Handles to deferred image conversions.
- pub deferred_images: HashMap<usize, (Deferred<StrResult<EncodedImage>>, Span)>,
- /// Deduplicates gradients used across the document.
- pub gradients: Remapper<PdfGradient>,
- /// Deduplicates tilings used across the document.
- pub tilings: Option<Box<TilingRemapper<R>>>,
- /// Deduplicates external graphics states used across the document.
- pub ext_gs: Remapper<ExtGState>,
- /// Deduplicates color glyphs.
- pub color_fonts: Option<Box<ColorFontMap<R>>>,
-
- // The fields below do not correspond to actual resources that will be
- // written in a dictionary, but are more meta-data about resources that
- // can't really live somewhere else.
- /// The number of glyphs for all referenced languages in the content stream.
- /// We keep track of this to determine the main document language.
- /// BTreeMap is used to write sorted list of languages to metadata.
- pub languages: BTreeMap<Lang, usize>,
-
- /// For each font a mapping from used glyphs to their text representation.
- /// This is used for the PDF's /ToUnicode map, and important for copy-paste
- /// and searching.
- ///
- /// Note that the text representation may contain multiple chars in case of
- /// ligatures or similar things, and it may have no entry in the font's cmap
- /// (or only a private-use codepoint), like the “Th” in Linux Libertine.
- ///
- /// A glyph may have multiple entries in the font's cmap, and even the same
- /// glyph can have a different text representation within one document.
- /// But /ToUnicode does not support that, so we just save the first occurrence.
- pub glyph_sets: HashMap<Font, BTreeMap<u16, EcoString>>,
- /// Same as `glyph_sets`, but for color fonts.
- pub color_glyph_sets: HashMap<Font, BTreeMap<u16, EcoString>>,
- /// Stores the glyph remapper for each font for the subsetter.
- pub glyph_remappers: HashMap<Font, GlyphRemapper>,
-}
-
-impl<R: Renumber> Renumber for Resources<R> {
- fn renumber(&mut self, offset: i32) {
- self.reference.renumber(offset);
-
- if let Some(color_fonts) = &mut self.color_fonts {
- color_fonts.resources.renumber(offset);
- }
-
- if let Some(tilings) = &mut self.tilings {
- tilings.resources.renumber(offset);
- }
- }
-}
-
-impl Default for Resources<()> {
- fn default() -> Self {
- Resources {
- reference: (),
- colors: ColorSpaces::default(),
- fonts: Remapper::new("F"),
- images: Remapper::new("Im"),
- deferred_images: HashMap::new(),
- gradients: Remapper::new("Gr"),
- tilings: None,
- ext_gs: Remapper::new("Gs"),
- color_fonts: None,
- languages: BTreeMap::new(),
- glyph_sets: HashMap::new(),
- color_glyph_sets: HashMap::new(),
- glyph_remappers: HashMap::new(),
- }
- }
-}
-
-impl Resources<()> {
- /// Associate a reference with this resource dictionary (and do so
- /// recursively for sub-resources).
- pub fn with_refs(self, refs: &ResourcesRefs) -> Resources<Ref> {
- Resources {
- reference: refs.reference,
- colors: self.colors,
- fonts: self.fonts,
- images: self.images,
- deferred_images: self.deferred_images,
- gradients: self.gradients,
- tilings: self
- .tilings
- .zip(refs.tilings.as_ref())
- .map(|(p, r)| Box::new(p.with_refs(r))),
- ext_gs: self.ext_gs,
- color_fonts: self
- .color_fonts
- .zip(refs.color_fonts.as_ref())
- .map(|(c, r)| Box::new(c.with_refs(r))),
- languages: self.languages,
- glyph_sets: self.glyph_sets,
- color_glyph_sets: self.color_glyph_sets,
- glyph_remappers: self.glyph_remappers,
- }
- }
-}
-
-impl<R> Resources<R> {
- /// Run a function on this resource dictionary and all
- /// of its sub-resources.
- pub fn traverse<P>(&self, process: &mut P) -> SourceResult<()>
- where
- P: FnMut(&Self) -> SourceResult<()>,
- {
- process(self)?;
- if let Some(color_fonts) = &self.color_fonts {
- color_fonts.resources.traverse(process)?;
- }
- if let Some(tilings) = &self.tilings {
- tilings.resources.traverse(process)?;
- }
- Ok(())
- }
-}
-
-/// References for a resource tree.
-///
-/// This structure is a tree too, that should have the same structure as the
-/// corresponding `Resources`.
-pub struct ResourcesRefs {
- pub reference: Ref,
- pub color_fonts: Option<Box<ResourcesRefs>>,
- pub tilings: Option<Box<ResourcesRefs>>,
-}
-
-impl Renumber for ResourcesRefs {
- fn renumber(&mut self, offset: i32) {
- self.reference.renumber(offset);
- if let Some(color_fonts) = &mut self.color_fonts {
- color_fonts.renumber(offset);
- }
- if let Some(tilings) = &mut self.tilings {
- tilings.renumber(offset);
- }
- }
-}
-
-/// Allocate references for all resource dictionaries.
-pub fn alloc_resources_refs(
- context: &WithResources,
-) -> SourceResult<(PdfChunk, ResourcesRefs)> {
- let mut chunk = PdfChunk::new();
- /// Recursively explore resource dictionaries and assign them references.
- fn refs_for(resources: &Resources<()>, chunk: &mut PdfChunk) -> ResourcesRefs {
- ResourcesRefs {
- reference: chunk.alloc(),
- color_fonts: resources
- .color_fonts
- .as_ref()
- .map(|c| Box::new(refs_for(&c.resources, chunk))),
- tilings: resources
- .tilings
- .as_ref()
- .map(|p| Box::new(refs_for(&p.resources, chunk))),
- }
- }
-
- let refs = refs_for(&context.resources, &mut chunk);
- Ok((chunk, refs))
-}
-
-/// Write the resource dictionaries that will be referenced by all pages.
-///
-/// We add a reference to this dictionary to each page individually instead of
-/// to the root node of the page tree because using the resource inheritance
-/// feature breaks PDF merging with Apple Preview.
-///
-/// Also write resource dictionaries for Type3 fonts and PDF patterns.
-pub fn write_resource_dictionaries(ctx: &WithEverything) -> SourceResult<(PdfChunk, ())> {
- let mut chunk = PdfChunk::new();
- let mut used_color_spaces = ColorSpaces::default();
-
- ctx.resources.traverse(&mut |resources| {
- used_color_spaces.merge(&resources.colors);
-
- let images_ref = chunk.alloc.bump();
- let patterns_ref = chunk.alloc.bump();
- let ext_gs_states_ref = chunk.alloc.bump();
- let color_spaces_ref = chunk.alloc.bump();
-
- let mut color_font_slices = Vec::new();
- let mut color_font_numbers = HashMap::new();
- if let Some(color_fonts) = &resources.color_fonts {
- for (_, font_slice) in color_fonts.iter() {
- color_font_numbers.insert(font_slice.clone(), color_font_slices.len());
- color_font_slices.push(font_slice);
- }
- }
- let color_font_remapper = Remapper {
- prefix: "Cf",
- to_pdf: color_font_numbers,
- to_items: color_font_slices,
- };
-
- resources
- .images
- .write(&ctx.references.images, &mut chunk.indirect(images_ref).dict());
-
- let mut patterns_dict = chunk.indirect(patterns_ref).dict();
- resources
- .gradients
- .write(&ctx.references.gradients, &mut patterns_dict);
- if let Some(p) = &resources.tilings {
- p.remapper.write(&ctx.references.tilings, &mut patterns_dict);
- }
- patterns_dict.finish();
-
- resources
- .ext_gs
- .write(&ctx.references.ext_gs, &mut chunk.indirect(ext_gs_states_ref).dict());
-
- let mut res_dict = chunk
- .indirect(resources.reference)
- .start::<pdf_writer::writers::Resources>();
- res_dict.pair(Name(b"XObject"), images_ref);
- res_dict.pair(Name(b"Pattern"), patterns_ref);
- res_dict.pair(Name(b"ExtGState"), ext_gs_states_ref);
- res_dict.pair(Name(b"ColorSpace"), color_spaces_ref);
-
- // TODO: can't this be an indirect reference too?
- let mut fonts_dict = res_dict.fonts();
- resources.fonts.write(&ctx.references.fonts, &mut fonts_dict);
- color_font_remapper.write(&ctx.references.color_fonts, &mut fonts_dict);
- fonts_dict.finish();
-
- res_dict.finish();
-
- let color_spaces = chunk.indirect(color_spaces_ref).dict();
- resources
- .colors
- .write_color_spaces(color_spaces, &ctx.globals.color_functions);
-
- Ok(())
- })?;
-
- used_color_spaces.write_functions(&mut chunk, &ctx.globals.color_functions);
-
- Ok((chunk, ()))
-}
-
-/// Assigns new, consecutive PDF-internal indices to items.
-pub struct Remapper<T> {
- /// The prefix to use when naming these resources.
- prefix: &'static str,
- /// Forwards from the items to the pdf indices.
- to_pdf: HashMap<T, usize>,
- /// Backwards from the pdf indices to the items.
- to_items: Vec<T>,
-}
-
-impl<T> Remapper<T>
-where
- T: Eq + Hash + Clone,
-{
- /// Create an empty mapping.
- pub fn new(prefix: &'static str) -> Self {
- Self { prefix, to_pdf: HashMap::new(), to_items: vec![] }
- }
-
- /// Insert an item in the mapping if it was not already present.
- pub fn insert(&mut self, item: T) -> usize {
- let to_layout = &mut self.to_items;
- *self.to_pdf.entry(item.clone()).or_insert_with(|| {
- let pdf_index = to_layout.len();
- to_layout.push(item);
- pdf_index
- })
- }
-
- /// All items in this
- pub fn items(&self) -> impl Iterator<Item = &T> + '_ {
- self.to_items.iter()
- }
-
- /// Write this list of items in a Resource dictionary.
- fn write(&self, mapping: &HashMap<T, Ref>, dict: &mut Dict) {
- for (number, item) in self.items().enumerate() {
- let name = eco_format!("{}{}", self.prefix, number);
- let reference = mapping[item];
- dict.pair(Name(name.as_bytes()), reference);
- }
- }
-}