Refactor PDF export a bit

This commit is contained in:
Laurenz 2023-10-04 14:41:21 +02:00
parent 7dc74b7281
commit 077218db3a
8 changed files with 133 additions and 161 deletions

View File

@ -1,5 +1,4 @@
use std::sync::Arc;
use once_cell::sync::Lazy;
use pdf_writer::types::DeviceNSubtype;
use pdf_writer::{writers, Chunk, Dict, Filter, Name, Ref};
@ -27,13 +26,18 @@ const HSL_S: Name<'static> = Name(b"S");
const HSL_L: Name<'static> = Name(b"L");
// The ICC profiles.
const SRGB_ICC: &[u8] = include_bytes!("./icc/sRGB-v4.icc");
const GRAY_ICC: &[u8] = include_bytes!("./icc/sGrey-v4.icc");
static SRGB_ICC_DEFLATED: Lazy<Vec<u8>> =
Lazy::new(|| deflate(include_bytes!("icc/sRGB-v4.icc")));
static GRAY_ICC_DEFLATED: Lazy<Vec<u8>> =
Lazy::new(|| deflate(include_bytes!("icc/sGrey-v4.icc")));
// The PostScript functions for color spaces.
const OKLAB_SOURCE: &str = include_str!("./postscript/oklab.ps");
const HSL_SOURCE: &str = include_str!("./postscript/hsl.ps");
const HSV_SOURCE: &str = include_str!("./postscript/hsv.ps");
static OKLAB_DEFLATED: Lazy<Vec<u8>> =
Lazy::new(|| deflate(minify(include_str!("postscript/oklab.ps")).as_bytes()));
static HSV_DEFLATED: Lazy<Vec<u8>> =
Lazy::new(|| deflate(minify(include_str!("postscript/hsl.ps")).as_bytes()));
static HSL_DEFLATED: Lazy<Vec<u8>> =
Lazy::new(|| deflate(minify(include_str!("postscript/hsv.ps")).as_bytes()));
/// The color spaces present in the PDF document
#[derive(Default)]
@ -161,87 +165,54 @@ impl ColorSpaces {
/// Write the necessary color spaces functions and ICC profiles to the
/// PDF file.
pub fn write_functions(&self, writer: &mut Chunk) {
// Write the Oklab function & color space
pub fn write_functions(&self, chunk: &mut Chunk) {
// Write the Oklab function & color space.
if let Some(oklab) = self.oklab {
let code = oklab_function();
writer
.post_script_function(oklab, &code)
chunk
.post_script_function(oklab, &OKLAB_DEFLATED)
.domain([0.0, 1.0, 0.0, 1.0, 0.0, 1.0])
.range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0])
.filter(Filter::FlateDecode);
}
// Write the HSV function & color space
// Write the HSV function & color space.
if let Some(hsv) = self.hsv {
let code = hsv_function();
writer
.post_script_function(hsv, &code)
chunk
.post_script_function(hsv, &HSV_DEFLATED)
.domain([0.0, 1.0, 0.0, 1.0, 0.0, 1.0])
.range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0])
.filter(Filter::FlateDecode);
}
// Write the HSL function & color space
// Write the HSL function & color space.
if let Some(hsl) = self.hsl {
let code = hsl_function();
writer
.post_script_function(hsl, &code)
chunk
.post_script_function(hsl, &HSL_DEFLATED)
.domain([0.0, 1.0, 0.0, 1.0, 0.0, 1.0])
.range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0])
.filter(Filter::FlateDecode);
}
// Write the sRGB color space
// Write the sRGB color space.
if let Some(srgb) = self.srgb {
let profile = srgb_icc();
writer
.icc_profile(srgb, &profile)
chunk
.icc_profile(srgb, &SRGB_ICC_DEFLATED)
.n(3)
.range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]);
.range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0])
.filter(Filter::FlateDecode);
}
// Write the gray color space
// Write the gray color space.
if let Some(gray) = self.d65_gray {
let profile = gray_icc();
writer.icc_profile(gray, &profile).n(1).range([0.0, 1.0]);
chunk
.icc_profile(gray, &GRAY_ICC_DEFLATED)
.n(1)
.range([0.0, 1.0])
.filter(Filter::FlateDecode);
}
}
}
/// Deflated sRGB ICC profile
#[comemo::memoize]
fn srgb_icc() -> Arc<Vec<u8>> {
Arc::new(deflate(SRGB_ICC))
}
/// Deflated gray ICC profile
#[comemo::memoize]
fn gray_icc() -> Arc<Vec<u8>> {
Arc::new(deflate(GRAY_ICC))
}
/// Deflated Oklab PostScript function
#[comemo::memoize]
fn oklab_function() -> Arc<Vec<u8>> {
let code = minify(OKLAB_SOURCE);
Arc::new(deflate(code.as_bytes()))
}
/// Deflated HSV PostScript function
#[comemo::memoize]
fn hsv_function() -> Arc<Vec<u8>> {
let code = minify(HSV_SOURCE);
Arc::new(deflate(code.as_bytes()))
}
/// Deflated HSL PostScript function
#[comemo::memoize]
fn hsl_function() -> Arc<Vec<u8>> {
let code = minify(HSL_SOURCE);
Arc::new(deflate(code.as_bytes()))
}
/// This function removes comments, line spaces and carriage returns from a
/// PostScript program. This is necessary to optimize the size of the PDF file.
fn minify(source: &str) -> String {

View File

@ -1,23 +1,21 @@
use pdf_writer::Finish;
use crate::export::pdf::PdfContext;
use super::PdfContext;
/// A PDF external graphics state.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct ExternalGraphicsState {
pub struct ExtGState {
// In the range 0-255, needs to be divided before being written into the graphics state!
pub stroke_opacity: u8,
// In the range 0-255, needs to be divided before being written into the graphics state!
pub fill_opacity: u8,
}
impl Default for ExternalGraphicsState {
impl Default for ExtGState {
fn default() -> Self {
Self { stroke_opacity: 255, fill_opacity: 255 }
}
}
impl ExternalGraphicsState {
impl ExtGState {
pub fn uses_opacities(&self) -> bool {
self.stroke_opacity != 255 || self.fill_opacity != 255
}
@ -26,13 +24,12 @@ impl ExternalGraphicsState {
/// Embed all used external graphics states into the PDF.
#[tracing::instrument(skip_all)]
pub fn write_external_graphics_states(ctx: &mut PdfContext) {
for external_gs in ctx.ext_gs_map.items() {
let gs_ref = ctx.alloc.bump();
ctx.ext_gs_refs.push(gs_ref);
let mut gs = ctx.writer.ext_graphics(gs_ref);
gs.non_stroking_alpha(external_gs.fill_opacity as f32 / 255.0)
for external_gs in ctx.extg_map.items() {
let id = ctx.alloc.bump();
ctx.ext_gs_refs.push(id);
ctx.pdf
.ext_graphics(id)
.non_stroking_alpha(external_gs.fill_opacity as f32 / 255.0)
.stroking_alpha(external_gs.stroke_opacity as f32 / 255.0);
gs.finish();
}
}

View File

@ -58,7 +58,7 @@ pub fn write_fonts(ctx: &mut PdfContext) {
};
// Write the base font object referencing the CID font.
ctx.writer
ctx.pdf
.type0_font(type0_ref)
.base_font(Name(base_font_type0.as_bytes()))
.encoding_predefined(Name(b"Identity-H"))
@ -66,7 +66,7 @@ pub fn write_fonts(ctx: &mut PdfContext) {
.to_unicode(cmap_ref);
// Write the CID font referencing the font descriptor.
let mut cid = ctx.writer.cid_font(cid_ref);
let mut cid = ctx.pdf.cid_font(cid_ref);
cid.subtype(if is_cff { CidFontType::Type0 } else { CidFontType::Type2 });
cid.base_font(Name(base_font.as_bytes()));
cid.system_info(SYSTEM_INFO);
@ -125,7 +125,7 @@ pub fn write_fonts(ctx: &mut PdfContext) {
let stem_v = 10.0 + 0.244 * (f32::from(ttf.weight().to_number()) - 50.0);
// Write the font descriptor (contains metrics about the font).
let mut font_descriptor = ctx.writer.font_descriptor(descriptor_ref);
let mut font_descriptor = ctx.pdf.font_descriptor(descriptor_ref);
font_descriptor
.name(Name(base_font.as_bytes()))
.flags(flags)
@ -147,13 +147,13 @@ pub fn write_fonts(ctx: &mut PdfContext) {
// Write the /ToUnicode character map, which maps glyph ids back to
// unicode codepoints to enable copying out of the PDF.
let cmap = create_cmap(ttf, glyph_set);
ctx.writer.cmap(cmap_ref, &cmap.finish());
ctx.pdf.cmap(cmap_ref, &cmap.finish());
// Subset and write the font's bytes.
let glyphs: Vec<_> = glyph_set.keys().copied().collect();
let data = subset_font(font, &glyphs);
let mut stream = ctx.writer.stream(data_ref, &data);
let mut stream = ctx.pdf.stream(data_ref, &data);
stream.filter(Filter::FlateDecode);
if is_cff {
stream.pair(Name(b"Subtype"), Name(b"CIDFontType0C"));

View File

@ -35,7 +35,7 @@ pub fn write_gradients(ctx: &mut PdfContext) {
let mut shading_pattern = match &gradient {
Gradient::Linear(linear) => {
let shading_function = shading_function(ctx, &gradient);
let mut shading_pattern = ctx.writer.shading_pattern(shading);
let mut shading_pattern = ctx.pdf.shading_pattern(shading);
let mut shading = shading_pattern.function_shading();
shading.shading_type(FunctionShadingType::Axial);
@ -108,7 +108,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref {
// These need to be individual function to encode 360.0 correctly.
let func1 = ctx.alloc.bump();
ctx.writer
ctx.pdf
.exponential_function(func1)
.range(gradient.space().range())
.c0(gradient.space().convert(first.0))
@ -117,7 +117,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref {
.n(1.0);
let func2 = ctx.alloc.bump();
ctx.writer
ctx.pdf
.exponential_function(func2)
.range(gradient.space().range())
.c0([1.0, s1 * (1.0 - t) + s2 * t, x1 * (1.0 - t) + x2 * t])
@ -126,7 +126,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref {
.n(1.0);
let func3 = ctx.alloc.bump();
ctx.writer
ctx.pdf
.exponential_function(func3)
.range(gradient.space().range())
.c0([0.0, s1 * (1.0 - t) + s2 * t, x1 * (1.0 - t) + x2 * t])
@ -157,7 +157,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref {
bounds.pop();
// Create the stitching function.
ctx.writer
ctx.pdf
.stitching_function(function)
.domain([0.0, 1.0])
.range(gradient.space().range())
@ -178,7 +178,7 @@ fn single_gradient(
) -> Ref {
let reference = ctx.alloc.bump();
ctx.writer
ctx.pdf
.exponential_function(reference)
.range(color_space.range())
.c0(color_space.convert(first_color))

View File

@ -15,26 +15,28 @@ use crate::{
pub fn write_images(ctx: &mut PdfContext) {
for image in ctx.image_map.items() {
let image_ref = ctx.alloc.bump();
let icc_ref = ctx.alloc.bump();
ctx.image_refs.push(image_ref);
let width = image.width();
let height = image.height();
// Add the primary image.
match image.kind() {
ImageKind::Raster(raster) => {
// TODO: Error if image could not be encoded.
let (data, filter, has_color) = encode_image(raster);
let mut image = ctx.writer.image_xobject(image_ref, &data);
let (data, filter, has_color) = encode_raster_image(raster);
let width = image.width();
let height = image.height();
let mut image = ctx.pdf.image_xobject(image_ref, &data);
image.filter(filter);
image.width(width as i32);
image.height(height as i32);
image.bits_per_component(8);
let mut icc_ref = None;
let space = image.color_space();
if raster.icc().is_some() {
space.icc_based(icc_ref);
let id = ctx.alloc.bump();
space.icc_based(id);
icc_ref = Some(id);
} else if has_color {
ctx.colors.write(ColorSpace::Srgb, space, &mut ctx.alloc);
} else {
@ -49,7 +51,7 @@ pub fn write_images(ctx: &mut PdfContext) {
image.s_mask(mask_ref);
image.finish();
let mut mask = ctx.writer.image_xobject(mask_ref, &alpha_data);
let mut mask = ctx.pdf.image_xobject(mask_ref, &alpha_data);
mask.filter(alpha_filter);
mask.width(width as i32);
mask.height(height as i32);
@ -59,9 +61,9 @@ pub fn write_images(ctx: &mut PdfContext) {
image.finish();
}
if let Some(icc) = raster.icc() {
if let (Some(icc), Some(icc_ref)) = (raster.icc(), icc_ref) {
let compressed = deflate(icc);
let mut stream = ctx.writer.icc_profile(icc_ref, &compressed);
let mut stream = ctx.pdf.icc_profile(icc_ref, &compressed);
stream.filter(Filter::FlateDecode);
if has_color {
stream.n(3);
@ -79,7 +81,7 @@ pub fn write_images(ctx: &mut PdfContext) {
let next_ref = svg2pdf::convert_tree_into(
tree,
svg2pdf::Options::default(),
&mut ctx.writer,
&mut ctx.pdf,
image_ref,
);
ctx.alloc = next_ref;
@ -95,7 +97,7 @@ pub fn write_images(ctx: &mut PdfContext) {
/// Skips the alpha channel as that's encoded separately.
#[comemo::memoize]
#[tracing::instrument(skip_all)]
fn encode_image(image: &RasterImage) -> (Arc<Vec<u8>>, Filter, bool) {
fn encode_raster_image(image: &RasterImage) -> (Arc<Vec<u8>>, Filter, bool) {
let dynamic = image.dynamic();
match (image.format(), dynamic) {
// 8-bit gray JPEG.

View File

@ -30,7 +30,7 @@ use crate::geom::{Abs, Dir, Em};
use crate::image::Image;
use crate::model::Introspector;
use extg::ExternalGraphicsState;
use extg::ExtGState;
/// Export a document into a PDF file.
///
@ -45,28 +45,21 @@ pub fn pdf(document: &Document) -> Vec<u8> {
extg::write_external_graphics_states(&mut ctx);
page::write_page_tree(&mut ctx);
write_catalog(&mut ctx);
ctx.writer.finish()
ctx.pdf.finish()
}
/// Context for exporting a whole PDF document.
pub struct PdfContext<'a> {
/// The document that we're currently exporting.
document: &'a Document,
/// An introspector for the document, used to resolve locations links and
/// the document outline.
introspector: Introspector,
writer: Pdf,
colors: ColorSpaces,
/// The writer we are writing the PDF into.
pdf: Pdf,
/// Content of exported pages.
pages: Vec<Page>,
page_heights: Vec<f32>,
alloc: Ref,
page_tree_ref: Ref,
font_refs: Vec<Ref>,
image_refs: Vec<Ref>,
gradient_refs: Vec<Ref>,
ext_gs_refs: Vec<Ref>,
page_refs: Vec<Ref>,
font_map: Remapper<Font>,
image_map: Remapper<Image>,
gradient_map: Remapper<PdfGradient>,
ext_gs_map: Remapper<ExternalGraphicsState>,
/// For each font a mapping from used glyphs to their text representation.
/// May contain multiple chars in case of ligatures or similar things. The
/// same glyph can have a different text representation within one document,
@ -74,7 +67,35 @@ pub struct PdfContext<'a> {
/// PDF's /ToUnicode map for glyphs that don't have an entry in the font's
/// cmap. This is important for copy-paste and searching.
glyph_sets: HashMap<Font, BTreeMap<u16, EcoString>>,
/// The number of glyphs for all referenced languages in the document.
/// We keep track of this to determine the main document language.
languages: HashMap<Lang, usize>,
/// Allocator for indirect reference IDs.
alloc: Ref,
/// The ID of the page tree.
page_tree_ref: Ref,
/// The IDs of written pages.
page_refs: Vec<Ref>,
/// The IDs of written fonts.
font_refs: Vec<Ref>,
/// The IDs of written images.
image_refs: Vec<Ref>,
/// The IDs of written gradients.
gradient_refs: Vec<Ref>,
/// The IDs of written external graphics states.
ext_gs_refs: Vec<Ref>,
/// Handles color space writing.
colors: ColorSpaces,
/// Deduplicates fonts used across the document.
font_map: Remapper<Font>,
/// Deduplicates images used across the document.
image_map: Remapper<Image>,
/// Deduplicates gradients used across the document.
gradient_map: Remapper<PdfGradient>,
/// Deduplicates external graphics states used across the document.
extg_map: Remapper<ExtGState>,
}
impl<'a> PdfContext<'a> {
@ -84,10 +105,10 @@ impl<'a> PdfContext<'a> {
Self {
document,
introspector: Introspector::new(&document.pages),
writer: Pdf::new(),
colors: ColorSpaces::default(),
pdf: Pdf::new(),
pages: vec![],
page_heights: vec![],
glyph_sets: HashMap::new(),
languages: HashMap::new(),
alloc,
page_tree_ref,
page_refs: vec![],
@ -95,12 +116,11 @@ impl<'a> PdfContext<'a> {
image_refs: vec![],
gradient_refs: vec![],
ext_gs_refs: vec![],
colors: ColorSpaces::default(),
font_map: Remapper::new(),
image_map: Remapper::new(),
gradient_map: Remapper::new(),
ext_gs_map: Remapper::new(),
glyph_sets: HashMap::new(),
languages: HashMap::new(),
extg_map: Remapper::new(),
}
}
}
@ -127,7 +147,7 @@ fn write_catalog(ctx: &mut PdfContext) {
let page_labels = write_page_labels(ctx);
// Write the document information.
let mut info = ctx.writer.document_info(ctx.alloc.bump());
let mut info = ctx.pdf.document_info(ctx.alloc.bump());
let mut xmp = XmpWriter::new();
if let Some(title) = &ctx.document.title {
info.title(TextStr(title));
@ -160,13 +180,13 @@ fn write_catalog(ctx: &mut PdfContext) {
let xmp_buf = xmp.finish(None);
let meta_ref = ctx.alloc.bump();
let mut meta_stream = ctx.writer.stream(meta_ref, xmp_buf.as_bytes());
meta_stream.pair(Name(b"Type"), Name(b"Metadata"));
meta_stream.pair(Name(b"Subtype"), Name(b"XML"));
meta_stream.finish();
ctx.pdf
.stream(meta_ref, xmp_buf.as_bytes())
.pair(Name(b"Type"), Name(b"Metadata"))
.pair(Name(b"Subtype"), Name(b"XML"));
// Write the document catalog.
let mut catalog = ctx.writer.catalog(ctx.alloc.bump());
let mut catalog = ctx.pdf.catalog(ctx.alloc.bump());
catalog.pages(ctx.page_tree_ref);
catalog.viewer_preferences().direction(dir);
catalog.pair(Name(b"Metadata"), meta_ref);
@ -215,7 +235,7 @@ fn write_page_labels(ctx: &mut PdfContext) -> Vec<(NonZeroUsize, Ref)> {
}
let id = ctx.alloc.bump();
let mut entry = ctx.writer.indirect(id).start::<PageLabel>();
let mut entry = ctx.pdf.indirect(id).start::<PageLabel>();
// Only add what is actually provided. Don't add empty prefix string if
// it wasn't given for example.
@ -309,17 +329,3 @@ impl EmExt for Em {
1000.0 * self.get() as f32
}
}
/// Additional methods for [`Ref`].
trait RefExt {
/// Bump the reference up by one and return the previous one.
fn bump(&mut self) -> Self;
}
impl RefExt for Ref {
fn bump(&mut self) -> Self {
let prev = *self;
*self = Self::new(prev.get() + 1);
prev
}
}

View File

@ -92,7 +92,7 @@ pub fn write_outline(ctx: &mut PdfContext) -> Option<Ref> {
prev_ref = Some(write_outline_item(ctx, node, root_id, prev_ref, i + 1 == len));
}
ctx.writer
ctx.pdf
.outline(root_id)
.first(start_ref)
.last(Ref::new(ctx.alloc.get() - 1))
@ -140,7 +140,7 @@ fn write_outline_item(
let id = ctx.alloc.bump();
let next_ref = Ref::new(id.get() + node.len() as i32);
let mut outline = ctx.writer.outline_item(id);
let mut outline = ctx.pdf.outline_item(id);
outline.parent(parent_ref);
if !is_last {
@ -164,11 +164,11 @@ fn write_outline_item(
let loc = node.element.location().unwrap();
let pos = ctx.introspector.position(loc);
let index = pos.page.get() - 1;
if let Some(&height) = ctx.page_heights.get(index) {
if let Some(page) = ctx.pages.get(index) {
let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
outline.dest().page(ctx.page_refs[index]).xyz(
pos.point.x.to_f32(),
height - y.to_f32(),
(page.size.y - y).to_f32(),
None,
);
}

View File

@ -8,7 +8,7 @@ use pdf_writer::types::{
use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str};
use super::color::PaintEncode;
use super::extg::ExternalGraphicsState;
use super::extg::ExtGState;
use super::{deflate, AbsExt, EmExt, PdfContext};
use crate::doc::{Destination, Frame, FrameItem, GroupItem, Meta, TextItem};
use crate::eval::Repr;
@ -32,7 +32,6 @@ pub fn construct_pages(ctx: &mut PdfContext, frames: &[Frame]) {
pub fn construct_page(ctx: &mut PdfContext, frame: &Frame) {
let page_ref = ctx.alloc.bump();
ctx.page_refs.push(page_ref);
ctx.page_heights.push(frame.height().to_f32());
let mut ctx = PageContext {
parent: ctx,
@ -81,7 +80,7 @@ pub fn write_page_tree(ctx: &mut PdfContext) {
write_page(ctx, i);
}
let mut pages = ctx.writer.pages(ctx.page_tree_ref);
let mut pages = ctx.pdf.pages(ctx.page_tree_ref);
pages
.count(ctx.page_refs.len() as i32)
.kids(ctx.page_refs.iter().copied());
@ -115,7 +114,7 @@ pub fn write_page_tree(ctx: &mut PdfContext) {
patterns.finish();
let mut ext_gs_states = resources.ext_g_states();
for (gs_ref, gs) in ctx.ext_gs_map.pdf_indices(&ctx.ext_gs_refs) {
for (gs_ref, gs) in ctx.extg_map.pdf_indices(&ctx.ext_gs_refs) {
let name = eco_format!("Gs{}", gs);
ext_gs_states.pair(Name(name.as_bytes()), gs_ref);
}
@ -125,7 +124,7 @@ pub fn write_page_tree(ctx: &mut PdfContext) {
pages.finish();
// Write all of the functions used by the document.
ctx.colors.write_functions(&mut ctx.writer);
ctx.colors.write_functions(&mut ctx.pdf);
}
/// Write a page tree node.
@ -134,7 +133,7 @@ fn write_page(ctx: &mut PdfContext, i: usize) {
let page = &ctx.pages[i];
let content_id = ctx.alloc.bump();
let mut page_writer = ctx.writer.page(page.id);
let mut page_writer = ctx.pdf.page(page.id);
page_writer.parent(ctx.page_tree_ref);
let w = page.size.x.to_f32();
@ -172,13 +171,13 @@ fn write_page(ctx: &mut PdfContext, i: usize) {
let index = pos.page.get() - 1;
let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
if let Some(&height) = ctx.page_heights.get(index) {
if let Some(page) = ctx.pages.get(index) {
annotation
.action()
.action_type(ActionType::GoTo)
.destination()
.page(ctx.page_refs[index])
.xyz(pos.point.x.to_f32(), height - y.to_f32(), None);
.xyz(pos.point.x.to_f32(), (page.size.y - y).to_f32(), None);
}
}
@ -186,7 +185,7 @@ fn write_page(ctx: &mut PdfContext, i: usize) {
page_writer.finish();
let data = deflate(&page.content);
ctx.writer.stream(content_id, &data).filter(Filter::FlateDecode);
ctx.pdf.stream(content_id, &data).filter(Filter::FlateDecode);
}
/// Data for an exported page.
@ -231,7 +230,7 @@ struct State {
font: Option<(Font, Abs)>,
fill: Option<Paint>,
fill_space: Option<Name<'static>>,
external_graphics_state: Option<ExternalGraphicsState>,
external_graphics_state: Option<ExtGState>,
stroke: Option<FixedStroke>,
stroke_space: Option<Name<'static>>,
}
@ -287,11 +286,11 @@ impl PageContext<'_, '_> {
self.state = self.saves.pop().expect("missing state save");
}
fn set_external_graphics_state(&mut self, graphics_state: &ExternalGraphicsState) {
fn set_external_graphics_state(&mut self, graphics_state: &ExtGState) {
let current_state = self.state.external_graphics_state.as_ref();
if current_state != Some(graphics_state) {
self.parent.ext_gs_map.insert(*graphics_state);
let name = eco_format!("Gs{}", self.parent.ext_gs_map.map(graphics_state));
self.parent.extg_map.insert(*graphics_state);
let name = eco_format!("Gs{}", self.parent.extg_map.map(graphics_state));
self.content.set_parameters(Name(name.as_bytes()));
if graphics_state.uses_opacities() {
@ -321,10 +320,7 @@ impl PageContext<'_, '_> {
color.alpha().map_or(255, |v| (v * 255.0).round() as u8)
})
.unwrap_or(255);
self.set_external_graphics_state(&ExternalGraphicsState {
stroke_opacity,
fill_opacity,
});
self.set_external_graphics_state(&ExtGState { stroke_opacity, fill_opacity });
}
fn transform(&mut self, transform: Transform) {