mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #2696 from Silvertree2010/fix/2674-pdf-import-lines-with-line-width
Import PDF 0 w strokes as hairlines instead of dropping them
This commit is contained in:
@@ -951,8 +951,17 @@ fn mean_scale(a: Affine) -> f64 {
|
||||
a.determinant().abs().sqrt()
|
||||
}
|
||||
|
||||
/// Width a PDF line width of 0 imports as, points: a print hairline. In PDF `0 w` is the thinnest
|
||||
/// line the device can draw (ISO 32000-1, 8.4.3.2), not no line.
|
||||
const HAIRLINE: f64 = 0.25;
|
||||
|
||||
/// Document width of the stroke `p` drawn at `scale`: `0 w` becomes a [`HAIRLINE`].
|
||||
fn line_width(p: &StrokeProps, scale: f64) -> f64 {
|
||||
if p.line_width == 0.0 { HAIRLINE } else { p.line_width as f64 * scale }
|
||||
}
|
||||
|
||||
fn stroke_layer(paint: Paint, opacity: f32, p: &StrokeProps, scale: f64) -> StrokeLayer {
|
||||
let mut st = StrokeLayer::new(paint, p.line_width as f64 * scale);
|
||||
let mut st = StrokeLayer::new(paint, line_width(p, scale));
|
||||
st.opacity = opacity;
|
||||
st.cap = match p.line_cap {
|
||||
kurbo::Cap::Butt => LineCap::Butt,
|
||||
@@ -1733,7 +1742,7 @@ impl<'p> Builder<'p> {
|
||||
None => named(&info.face),
|
||||
};
|
||||
let (paint, opacity) = self.paint(paint, stroke.is_some(), self.walked(true));
|
||||
let stroke = stroke.map(|p| (paint.clone(), p.line_width as f64 * scale));
|
||||
let stroke = stroke.map(|p| (paint.clone(), line_width(p, scale)));
|
||||
let look = Look {
|
||||
font: key,
|
||||
family: info.family,
|
||||
@@ -1850,7 +1859,7 @@ impl<'a> Device<'a> for Builder<'_> {
|
||||
let (paint, opacity) = self.paint(paint, stroke, walked);
|
||||
// A gradient that doesn't extend past its ends paints only between them.
|
||||
let width = match draw_mode {
|
||||
PathDrawMode::Stroke(p) => p.line_width as f64 * mean_scale(transform),
|
||||
PathDrawMode::Stroke(p) => line_width(p, mean_scale(transform)),
|
||||
PathDrawMode::Fill(_) => 0.0,
|
||||
};
|
||||
let band = shading.and_then(|sp| extend_clip(sp, bp.bounding_box().inflate(width, width)));
|
||||
|
||||
@@ -644,3 +644,16 @@ fn clips_with_the_page_as_bounding_box_still_clip() {
|
||||
assert!(!clipped("0 0 100 100 re"), "the page");
|
||||
assert!(!clipped("50 -60 m 160 50 l 50 160 l -60 50 l h"), "a turned square around the page");
|
||||
}
|
||||
|
||||
/// Issue #2674: `0 w` is the thinnest line the device can draw (ISO 32000-1, 8.4.3.2), not no
|
||||
/// line: it imports as a visible hairline and survives a PDF round trip.
|
||||
#[test]
|
||||
fn zero_width_lines_import_as_hairlines() {
|
||||
let d = open(&one_page("0 0 0 RG 0 w 10 50 m 90 50 l S 0.9 g 0 w 20 60 30 20 re b 1 w 10 20 m 90 20 l S", "", &[]));
|
||||
let widths = |d: &Document| -> Vec<f64> { all(d).iter().filter_map(|n| n.appearance.stroke().map(|s| s.width)).collect() };
|
||||
let w = widths(&d);
|
||||
assert_eq!(w.len(), 3, "{w:?}");
|
||||
assert!(w.iter().all(|w| *w > 0.0 && *w <= 1.0), "zero width strokes become hairlines: {w:?}");
|
||||
let back = widths(&roundtrip(&d));
|
||||
assert_eq!(back.len(), 3, "hairlines survive the PDF round trip: {back:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user