mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-15 06:52:23 +00:00
Print PDFs written through krilla separated wrongly: transparency groups blended in RGB
(K 50% over C100 separated as C84 M34 Y32 K37 instead of C50 K50), and there was no
overprint, knockout, DeviceN, soft-mask backdrop or print output intent.
Patches in vendor/krilla/patches/, recorded in README.craft.md and SOURCE.json:
- 02 blending color space: GroupColorSpace { Rgb (default, unchanged output), Cmyk }
- 03 overprint: Surface::push_overprint (/op /OP /OPM)
- 04 knockout groups: Surface::push_group(GroupOptions { isolated, knockout })
- 05 soft-mask backdrop and transfer function (/BC /TR)
- 06 DeviceN and NChannel colors for fills and strokes
- 07 DeviceN images and shadings
- 08 print output intent (GTS_PDFX), /Trapped, PDF/X version markers
craft_pdf::write::print::settings(PrintColor) turns them on for print export.
124 lines
3.5 KiB
Rust
124 lines
3.5 KiB
Rust
//! Tiling patterns.
|
|
|
|
use std::hash::{Hash, Hasher};
|
|
use std::ops::DerefMut;
|
|
|
|
use pdf_writer::types::{PaintType, TilingType};
|
|
use pdf_writer::{Finish, Ref};
|
|
|
|
use crate::chunk_container::ChunkContainer;
|
|
use crate::geom::Transform;
|
|
use crate::num::NormalizedF32;
|
|
use crate::resource;
|
|
use crate::resource::Resourceable;
|
|
use crate::serialize::{Cacheable, SerializeContext};
|
|
use crate::stream::StreamBuilder;
|
|
use crate::stream::{FilterStreamBuilder, Stream};
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub(crate) struct TilingPattern {
|
|
stream: Stream,
|
|
transform: Transform,
|
|
base_opacity: NormalizedF32,
|
|
width: f32,
|
|
height: f32,
|
|
}
|
|
|
|
impl Eq for TilingPattern {}
|
|
|
|
impl Hash for TilingPattern {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
self.stream.hash(state);
|
|
self.transform.hash(state);
|
|
self.base_opacity.hash(state);
|
|
self.width.to_bits().hash(state);
|
|
self.height.to_bits().hash(state);
|
|
}
|
|
}
|
|
|
|
impl TilingPattern {
|
|
pub(crate) fn new(
|
|
stream: Stream,
|
|
transform: Transform,
|
|
base_opacity: NormalizedF32,
|
|
width: f32,
|
|
height: f32,
|
|
serializer_context: &mut SerializeContext,
|
|
chunk_container: &mut ChunkContainer,
|
|
) -> Self {
|
|
// stroke/fill opacity doesn't work consistently across different viewers for patterns,
|
|
// so instead we simulate it ourselves.
|
|
let pattern_stream = if base_opacity == NormalizedF32::ONE {
|
|
stream
|
|
} else {
|
|
let stream = {
|
|
let mut builder = StreamBuilder::new(serializer_context, chunk_container);
|
|
let mut surface = builder.surface();
|
|
surface.draw_opacified_stream(base_opacity, stream);
|
|
surface.finish();
|
|
builder.finish()
|
|
};
|
|
|
|
stream
|
|
};
|
|
|
|
Self {
|
|
stream: pattern_stream,
|
|
transform,
|
|
base_opacity,
|
|
width,
|
|
height,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Cacheable for TilingPattern {
|
|
fn serialize(
|
|
self,
|
|
sc: &mut SerializeContext,
|
|
chunk_container: &mut ChunkContainer,
|
|
root_ref: Ref,
|
|
) {
|
|
let mut chunk = sc.new_chunk();
|
|
|
|
for validation_error in self.stream.validation_errors {
|
|
sc.register_validation_error(validation_error);
|
|
}
|
|
|
|
if sc.uses_transparency(&self.stream.resource_dictionary) {
|
|
sc.mark_transparent(root_ref);
|
|
}
|
|
|
|
let pattern_stream = FilterStreamBuilder::new_from_content_stream(
|
|
&self.stream.content,
|
|
&sc.serialize_settings(),
|
|
)
|
|
.finish(&sc.serialize_settings());
|
|
let mut tiling_pattern = chunk.tiling_pattern(root_ref, pattern_stream.encoded_data());
|
|
pattern_stream.write_filters(tiling_pattern.deref_mut().deref_mut());
|
|
|
|
self.stream.resource_dictionary.to_pdf_resources(
|
|
&mut tiling_pattern,
|
|
sc,
|
|
&mut chunk_container.non_stream.resource_dictionaries,
|
|
);
|
|
|
|
let final_bbox = pdf_writer::Rect::new(0.0, 0.0, self.width, self.height);
|
|
|
|
tiling_pattern
|
|
.tiling_type(TilingType::ConstantSpacing)
|
|
.paint_type(PaintType::Colored)
|
|
.bbox(final_bbox)
|
|
.matrix(self.transform.to_pdf_transform())
|
|
.x_step(final_bbox.x2 - final_bbox.x1)
|
|
.y_step(final_bbox.y2 - final_bbox.y1);
|
|
|
|
tiling_pattern.finish();
|
|
chunk_container.streams.patterns.push(chunk);
|
|
}
|
|
}
|
|
|
|
impl Resourceable for TilingPattern {
|
|
type Resource = resource::Pattern;
|
|
}
|