mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
Merge branch 'pr-512' into batch/t5
This commit is contained in:
@@ -35,6 +35,8 @@ pub(crate) struct Exporter<'a> {
|
||||
pub links: Vec<(Rect, String)>,
|
||||
/// Above zero while drawing into a soft mask (nothing there is visible or clickable).
|
||||
pub(crate) masking: usize,
|
||||
/// Above zero while drawing a pattern tile (links and backdrops belong to the page).
|
||||
pub(crate) tiling: usize,
|
||||
/// The page's frame and the bleed its fills and clip reach out to.
|
||||
pub bleed_root: Option<(NodeId, f64)>,
|
||||
pub(crate) fonts: HashMap<u32, Option<krilla::text::Font>>,
|
||||
@@ -215,6 +217,7 @@ impl<'a> Exporter<'a> {
|
||||
page_ids: Vec::new(),
|
||||
links: Vec::new(),
|
||||
masking: 0,
|
||||
tiling: 0,
|
||||
bleed_root: None,
|
||||
fonts: HashMap::new(),
|
||||
images: HashMap::new(),
|
||||
@@ -246,7 +249,8 @@ impl<'a> Exporter<'a> {
|
||||
let op = n.opacity.clamp(0.0, 1.0);
|
||||
// Background blur and glass: an image drawn from what lies under the node, under its
|
||||
// content (nothing in a soft mask is seen).
|
||||
let back = if !as_mask && self.masking == 0 && figcraft_render::backdrop::has_effects(n) { self.backdrop(id) } else { None };
|
||||
let back =
|
||||
if !as_mask && self.masking == 0 && self.tiling == 0 && figcraft_render::backdrop::has_effects(n) { self.backdrop(id) } else { None };
|
||||
if fx.iter().any(|k| matches!(k, EffectKind::LayerBlur { .. } | EffectKind::Noise(_) | EffectKind::Texture(_))) {
|
||||
// Effects that change every pixel: the renderer draws the whole node.
|
||||
if let Some(b) = &back {
|
||||
@@ -521,14 +525,14 @@ impl<'a> Exporter<'a> {
|
||||
let mut sb = s.stream_builder();
|
||||
let mut ps = sb.surface();
|
||||
// Links and backdrops belong to the page, not to a tile.
|
||||
self.masking += 1;
|
||||
self.tiling += 1;
|
||||
let _ = figcraft_render::pattern::nested(|| {
|
||||
for (tx, ty) in &cell.tiles {
|
||||
self.node(&mut ps, src.id, Affine::translate((*tx, *ty)) * Affine::scale(cell.scale) * unrotated, 0, false);
|
||||
}
|
||||
Some(())
|
||||
});
|
||||
self.masking -= 1;
|
||||
self.tiling -= 1;
|
||||
ps.finish();
|
||||
sb.finish()
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ pub(crate) fn draw_text(ex: &mut Exporter, s: &mut Surface, n: &Node, xf: Affine
|
||||
if !affine_ok(gxf) {
|
||||
return;
|
||||
}
|
||||
if ex.masking == 0 {
|
||||
if ex.masking == 0 && ex.tiling == 0 {
|
||||
record_links(ex, &layout, gxf);
|
||||
}
|
||||
let (w, h) = (n.width, n.height);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
//! CMYK export converts the colours inside vector pattern tiles (#426).
|
||||
|
||||
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
||||
|
||||
use figcraft_color::{Color, Paint};
|
||||
use figcraft_doc::{Document, NodeKind, PrintSettings};
|
||||
use figcraft_pdf::{PdfOptions, export_pdf};
|
||||
use std::io::Read;
|
||||
|
||||
/// Every zlib stream of `pdf`, inflated and joined.
|
||||
fn streams(pdf: &[u8]) -> String {
|
||||
let mut out = String::new();
|
||||
let mut rest = pdf;
|
||||
while let Some(i) = rest.windows(7).position(|w| w == b"stream\n") {
|
||||
rest = &rest[i + 7..];
|
||||
let end = rest.windows(9).position(|w| w == b"endstream").unwrap();
|
||||
let mut bytes = Vec::new();
|
||||
if flate2::read::ZlibDecoder::new(&rest[..end]).read_to_end(&mut bytes).is_ok() {
|
||||
out.push_str(&String::from_utf8_lossy(&bytes));
|
||||
out.push('\n');
|
||||
}
|
||||
rest = &rest[end + 9..];
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn export(pattern: bool, cmyk: bool) -> (String, String) {
|
||||
let mut doc = Document::new("fixture");
|
||||
let page = doc.first_page().unwrap();
|
||||
let source = doc.create(page, NodeKind::Rectangle, "Source", 40.0, 0.0, 10.0, 10.0).unwrap();
|
||||
let green = Paint::solid(Color::from_hex("00FF00").unwrap());
|
||||
doc.get_mut(source).unwrap().fills = vec![green.clone()];
|
||||
let target = doc.create(page, NodeKind::Rectangle, "Target", 0.0, 0.0, 20.0, 20.0).unwrap();
|
||||
doc.get_mut(target).unwrap().fills = vec![if pattern { Paint::pattern(source.0) } else { green }];
|
||||
let opts = PdfOptions { print: PrintSettings { cmyk, ..Default::default() }, ..Default::default() };
|
||||
let pdf = export_pdf(&doc, &[target], &opts).unwrap();
|
||||
(String::from_utf8_lossy(&pdf).into_owned(), streams(&pdf))
|
||||
}
|
||||
|
||||
fn has_rgb_op(content: &str) -> bool {
|
||||
content.split_whitespace().any(|op| op == "rg" || op == "RG")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_cmyk_controls() {
|
||||
let (_, solid) = export(false, true);
|
||||
assert!(solid.contains("1 0 1 0 k"), "{solid}");
|
||||
assert!(!has_rgb_op(&solid), "{solid}");
|
||||
let (pdf, pattern) = export(true, false);
|
||||
assert!(pdf.contains("/PatternType 1"));
|
||||
assert!(pattern.contains("0 1 0 rg"), "{pattern}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_colors_follow_cmyk_setting() {
|
||||
let (pdf, content) = export(true, true);
|
||||
assert!(pdf.contains("/PatternType 1"));
|
||||
assert!(!has_rgb_op(&content), "RGB operator remains in CMYK pattern: {content}");
|
||||
assert!(content.contains("1 0 1 0 k"), "green must convert to CMYK: {content}");
|
||||
}
|
||||
Reference in New Issue
Block a user