diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..4b6c79f5 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,4 @@ +allow-unwrap-in-tests = true +allow-expect-in-tests = true +allow-panic-in-tests = true +allow-indexing-slicing-in-tests = true diff --git a/crates/pdf/src/cff.rs b/crates/pdf/src/cff.rs index 03493d81..ce39a732 100644 --- a/crates/pdf/src/cff.rs +++ b/crates/pdf/src/cff.rs @@ -646,12 +646,21 @@ impl T2<'_> { self.path.move_to((self.x, self.y)); self.open = true; } + /// A segment before the first moveto starts at the current point. + fn ensure_open(&mut self) { + if !self.open { + self.path.move_to((self.x, self.y)); + self.open = true; + } + } fn line_to(&mut self, dx: f64, dy: f64) { + self.ensure_open(); self.x += dx; self.y += dy; self.path.line_to((self.x, self.y)); } fn curve(&mut self, d: [f64; 6]) { + self.ensure_open(); let (x1, y1) = (self.x + d[0], self.y + d[1]); let (x2, y2) = (x1 + d[2], y1 + d[3]); self.x = x2 + d[4]; diff --git a/crates/pdf/src/lib.rs b/crates/pdf/src/lib.rs index 3db854fc..39d15074 100644 --- a/crates/pdf/src/lib.rs +++ b/crates/pdf/src/lib.rs @@ -7,6 +7,8 @@ //! children are the document's **layers**: one group per top-level optional-content group //! (Illustrator layers), with content outside them gathered into groups of its own. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + mod build; mod ccitt; mod cff; diff --git a/crates/pdf/src/page.rs b/crates/pdf/src/page.rs index c754c1dd..94f62933 100644 --- a/crates/pdf/src/page.rs +++ b/crates/pdf/src/page.rs @@ -332,7 +332,7 @@ impl<'a> Interp<'a> { // Painting. "S" => self.paint(false, None, true, depth), "s" => { - self.path.close_path(); + self.close_subpath(); self.paint(false, None, true, depth) } "f" | "F" => self.paint(true, Some(FillRule::NonZero), false, depth), @@ -340,11 +340,11 @@ impl<'a> Interp<'a> { "B" => self.paint(true, Some(FillRule::NonZero), true, depth), "B*" => self.paint(true, Some(FillRule::EvenOdd), true, depth), "b" => { - self.path.close_path(); + self.close_subpath(); self.paint(true, Some(FillRule::NonZero), true, depth) } "b*" => { - self.path.close_path(); + self.close_subpath(); self.paint(true, Some(FillRule::EvenOdd), true, depth) } "n" => self.paint(false, None, false, depth), @@ -549,6 +549,13 @@ impl<'a> Interp<'a> { } } + /// Close the current subpath; nothing to close without a current point. + fn close_subpath(&mut self) { + if self.cur.is_some() { + self.path.close_path(); + } + } + fn ensure_start(&mut self) { if self.cur.is_none() { self.path.move_to((0.0, 0.0)); diff --git a/crates/pdf/src/tests_import.rs b/crates/pdf/src/tests_import.rs index b6b96b80..e5daf94e 100644 --- a/crates/pdf/src/tests_import.rs +++ b/crates/pdf/src/tests_import.rs @@ -295,3 +295,12 @@ fn truncated_and_corrupt_eps_never_panic() { assert!(r.is_ok(), "{}", String::from_utf8_lossy(prog)); } } + +#[test] +fn closing_an_empty_path_is_a_no_op() { + // "s", "b" and "b*" closed the path without a current point (kurbo asserts on that). + for ops in ["s", "b", "b*", "0 0 m 50 0 l 50 50 l b s b*", "f s 10 10 m 20 20 l s"] { + let doc = parse(&page_pdf(ops, "", vec![])).unwrap(); + let _ = render(&doc); + } +}