mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
Merge PR #3327: Render: a five-point polyline is no longer painted as its bounding box
vendor/README.md: main's row plus the PR's hayro-interpret patch and tests, numbered 47 (35 is #3296's)
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+49
-19
@@ -447,36 +447,66 @@ pub(crate) fn path_as_rect(path: &BezPath) -> Option<Rect> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bbox = path.fast_bounding_box();
|
||||
let (min_x, min_y, max_x, max_y) = (bbox.min_x(), bbox.min_y(), bbox.max_x(), bbox.max_y());
|
||||
let mut corners = [false; 4];
|
||||
|
||||
let mut check_point = |p: Point| {
|
||||
corners[0] |= p.x.is_nearly_equal(min_x) && p.y.is_nearly_equal(min_y);
|
||||
corners[1] |= p.x.is_nearly_equal(min_x) && p.y.is_nearly_equal(max_y);
|
||||
corners[2] |= p.x.is_nearly_equal(max_x) && p.y.is_nearly_equal(min_y);
|
||||
corners[3] |= p.x.is_nearly_equal(max_x) && p.y.is_nearly_equal(max_y);
|
||||
};
|
||||
|
||||
// PdfCraft patch: the points have to trace the rectangle, not merely reach its corners. The
|
||||
// length check above admits one MoveTo and four LineTo as well as the intended one MoveTo,
|
||||
// three LineTo and one ClosePath, and asking only that each corner of the bounding box was
|
||||
// visited says nothing about the order or the steps between: a five-point polyline whose
|
||||
// ends reached every corner of its own bounding box was painted as that box, and a clip of
|
||||
// that shape was discarded. Four distinct points, each step along one axis, the last point
|
||||
// back at the first: that is a rectangle and nothing else is.
|
||||
let mut points: Vec<Point> = Vec::with_capacity(5);
|
||||
let mut closed = false;
|
||||
for (idx, el) in path.elements().iter().enumerate() {
|
||||
match el {
|
||||
PathEl::MoveTo(p) => {
|
||||
if idx != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
check_point(*p);
|
||||
points.push(*p);
|
||||
}
|
||||
PathEl::LineTo(l) => check_point(*l),
|
||||
PathEl::LineTo(p) => points.push(*p),
|
||||
PathEl::QuadTo(_, _) => return None,
|
||||
PathEl::CurveTo(_, _, _) => return None,
|
||||
PathEl::ClosePath => {}
|
||||
// A subpath that ends and starts again is more than one shape.
|
||||
PathEl::ClosePath => {
|
||||
if idx + 1 != path.elements().len() {
|
||||
return None;
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if corners[0] && corners[1] && corners[2] && corners[3] {
|
||||
Some(bbox)
|
||||
} else {
|
||||
None
|
||||
// Exactly equal, not nearly: `is_nearly_equal` allows a 256th of a path unit, and the matrix
|
||||
// is applied after this, so a `cm` of 10,000 turns that into tens of device units -- a path
|
||||
// that only nearly closes would be painted as the rectangle, with a side it does not have.
|
||||
// A rectangle's coordinates are equal in the content stream, not approximately equal, so
|
||||
// nothing that is one loses the fast path by this; a path that misses takes the general route
|
||||
// and is painted as written (#3327 review).
|
||||
let same = |a: Point, b: Point| a.x == b.x && a.y == b.y;
|
||||
// `m l l l l`: the fourth line goes back to the start, which `m l l l h` does with ClosePath.
|
||||
if !closed {
|
||||
if points.len() != 5 || !same(points[4], points[0]) {
|
||||
return None;
|
||||
}
|
||||
points.pop();
|
||||
}
|
||||
if points.len() != 4 {
|
||||
return None;
|
||||
}
|
||||
// Distinct, so a degenerate path is not one, and each step along one axis only: the four
|
||||
// points of an axis-aligned rectangle, in order round it.
|
||||
for (i, p) in points.iter().enumerate() {
|
||||
let q = points[(i + 1) % 4];
|
||||
let along_x = p.y == q.y && p.x != q.x;
|
||||
let along_y = p.x == q.x && p.y != q.y;
|
||||
if !(along_x || along_y) {
|
||||
return None;
|
||||
}
|
||||
if points.iter().skip(i + 1).any(|o| same(*p, *o)) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(path.fast_bounding_box())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user