continued work
This commit is contained in:
parent
c9f38685b4
commit
05f5bed1ad
|
|
@ -292,6 +292,12 @@ fn view_toggles_redraw_without_dirtying() {
|
|||
s.execute("view.overprintPreview", &json!({"on": false})).unwrap();
|
||||
}
|
||||
|
||||
/// Whether the content has a DeviceCMYK fill/stroke operator (`c m y k k` / `K`).
|
||||
fn has_cmyk_op(pdf: &str) -> bool {
|
||||
let toks: Vec<&str> = pdf.split_ascii_whitespace().collect();
|
||||
toks.windows(5).any(|w| (w[4] == "k" || w[4] == "K") && w[..4].iter().all(|t| t.parse::<f32>().is_ok()))
|
||||
}
|
||||
|
||||
fn pdf_text(doc: &Document) -> String {
|
||||
let bytes = drawcraft_pdf::export(doc, &drawcraft_pdf::PdfOptions { compress: false, created: Some(0), ..Default::default() }).unwrap();
|
||||
String::from_utf8_lossy(&bytes).into_owned()
|
||||
|
|
@ -302,17 +308,16 @@ fn pdf_cmyk_document_writes_device_cmyk() {
|
|||
let mut s = session();
|
||||
rect(&mut s, 0.0, 0.0, 10.0, 10.0, json!("#ff0000"));
|
||||
let rgb_pdf = pdf_text(&s.doc().unwrap().doc);
|
||||
assert!(!rgb_pdf.contains("DeviceCMYK"));
|
||||
assert!(!has_cmyk_op(&rgb_pdf) && rgb_pdf.contains(" rg"));
|
||||
s.execute("object.convertDocumentColorMode", &json!({"mode": "cmyk"})).unwrap();
|
||||
let pdf = pdf_text(&s.doc().unwrap().doc);
|
||||
std::fs::write("/tmp/claude-dbg.pdf", &pdf).ok();
|
||||
assert!(pdf.contains("DeviceCMYK"), "CMYK colours are written as DeviceCMYK");
|
||||
assert!(has_cmyk_op(&pdf), "CMYK colours are written with the DeviceCMYK `k` operator");
|
||||
// RGB colours pasted into a CMYK document are separated on export.
|
||||
let mut s2 = session();
|
||||
rect(&mut s2, 0.0, 0.0, 10.0, 10.0, json!("#336699"));
|
||||
let mut d = (*s2.doc().unwrap().doc).clone();
|
||||
d.color_mode = ColorMode::Cmyk;
|
||||
assert!(pdf_text(&d).contains("DeviceCMYK"));
|
||||
assert!(has_cmyk_op(&pdf_text(&d)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -82,15 +82,16 @@ impl BandMatrix {
|
|||
let (n, bw) = (self.n, self.bw);
|
||||
for i in 0..n {
|
||||
let mut s = b[i];
|
||||
for k in i.saturating_sub(bw)..i {
|
||||
s -= self.a[self.idx(i, k)] * b[k];
|
||||
let k0 = i.saturating_sub(bw);
|
||||
for (k, bk) in b[k0..i].iter().enumerate() {
|
||||
s -= self.a[self.idx(i, k0 + k)] * bk;
|
||||
}
|
||||
b[i] = s / self.a[self.idx(i, i)];
|
||||
}
|
||||
for i in (0..n).rev() {
|
||||
let mut s = b[i];
|
||||
for k in i + 1..(i + bw + 1).min(n) {
|
||||
s -= self.a[self.idx(k, i)] * b[k];
|
||||
for (k, bk) in b[i + 1..(i + bw + 1).min(n)].iter().enumerate() {
|
||||
s -= self.a[self.idx(i + 1 + k, i)] * bk;
|
||||
}
|
||||
b[i] = s / self.a[self.idx(i, i)];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -602,12 +602,10 @@ mod tests {
|
|||
let mut pd = PathData::single(SubPath::polyline(&[Point::new(80.0, 100.0), Point::new(120.0, 100.0)], false));
|
||||
let p = LiquifyParams { detail: 1.0, simplify: 0.0, ..prm(LiquifyKind::Twirl) };
|
||||
apply_stroke(&mut pd, &[c], &p, 0);
|
||||
// Rotation preserves the distance to the centre.
|
||||
// Rotation preserves every anchor's distance to the centre (ends at 20, the midpoint at 0).
|
||||
for (_, _, a) in pd.anchors() {
|
||||
let d0 = if a.p.x < 100.0 { 20.0 } else { 20.0 };
|
||||
if a.p.distance(c) > 1.0 {
|
||||
assert!((a.p.distance(c) - d0).abs() < 1e-6 || a.p.distance(c) < 20.0);
|
||||
}
|
||||
let d = a.p.distance(c);
|
||||
assert!((d - 20.0).abs() < 1e-9 || d < 1e-9, "{d}");
|
||||
}
|
||||
let first = pd.subpaths[0].anchors[0].p;
|
||||
assert!(first.y != 100.0 && (first.distance(c) - 20.0).abs() < 1e-9);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ impl Plane {
|
|||
pub const ALL: [Plane; 3] = [Plane::Left, Plane::Right, Plane::Ground];
|
||||
}
|
||||
|
||||
/// Plane Switching widget: (plane, face quad) list and the "no plane" circle (centre, radius).
|
||||
pub type WidgetGeom = (Vec<(Plane, [Point; 4])>, (Point, f64));
|
||||
|
||||
/// A 3×3 projective transform (row-major).
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Homography(pub [[f64; 3]; 3]);
|
||||
|
|
@ -350,7 +353,7 @@ impl PerspectiveGrid {
|
|||
}
|
||||
|
||||
/// Plane Switching widget geometry: (plane, quad) faces and the "no plane" circle (centre, r).
|
||||
pub fn widget(&self, doc: &Document, tol: f64) -> (Vec<(Plane, [Point; 4])>, (Point, f64)) {
|
||||
pub fn widget(&self, doc: &Document, tol: f64) -> WidgetGeom {
|
||||
let ab = doc.artboards.first().map(|a| a.rect).unwrap_or(Rect::new(0.0, 0.0, 612.0, 792.0));
|
||||
let s = 40.0 * tol;
|
||||
let (cx, cy) = (ab.x0 + 10.0 * tol + s / 2.0, ab.y0 + 10.0 * tol + s / 2.0);
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ pub fn menu_items() -> Vec<crate::menus::Item> {
|
|||
/// The app-bar workspace switcher's menu.
|
||||
pub fn popup(app: &mut DrawcraftApp, ui: &mut egui::Ui) {
|
||||
ui.set_min_width(210.0);
|
||||
ui.set_max_width(240.0);
|
||||
let mut clicked: Option<(&'static str, Value)> = None;
|
||||
for it in menu_items() {
|
||||
match it {
|
||||
|
|
@ -248,7 +249,7 @@ pub fn popup(app: &mut DrawcraftApp, ui: &mut egui::Ui) {
|
|||
Some(false) => format!(" {label}"),
|
||||
None => format!(" {label}"),
|
||||
};
|
||||
if ui.add(egui::Button::new(text).min_size(egui::vec2(ui.available_width(), 0.0))).clicked() {
|
||||
if ui.add(egui::Button::new(text).min_size(egui::vec2(220.0, 0.0))).clicked() {
|
||||
clicked = Some((id, if p.is_null() { json!({}) } else { p }));
|
||||
ui.close();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue