Files
craft-libs/vendor/krilla/src/graphics/tiling_pattern.rs
T
aiongg eedd0246df krilla: CMYK print color in the vendored krilla, and a craft-pdf print preset
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.
2026-10-20 03:41:13 +00:00

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;
}