Commit f5c28ec8 authored by RazrFalcon's avatar RazrFalcon

(cairo-backend) Fixed rendering of a zero length subpath with a square cap.

parent 244f30fe
......@@ -21,6 +21,7 @@ This changelog also contains an important changes in dependencies.
### Fixed
- (cairo-backend) Text layout.
- (cairo-backend) Rendering of a zero length subpath with a square cap.
- (resvg) Recursive SVG images via `image` tag.
- (resvg) Bbox calculation of the text with rotate.
- (qt-api) SVG from QByteArray loading when data is invalid.
......
......@@ -30,7 +30,7 @@ travis-ci = { repository = "RazrFalcon/resvg" }
[dependencies]
log = "0.4.5"
#usvg = "0.2"
usvg = { git = "https://github.com/RazrFalcon/usvg", rev = "9b7699a" }
usvg = { git = "https://github.com/RazrFalcon/usvg", rev = "f0890e5" }
#usvg = { path = "../usvg" }
# cairo backend
cairo-rs = { version = "0.5", features = ["png"], optional = true }
......
......@@ -20,7 +20,12 @@ pub fn draw(
opt: &Options,
cr: &cairo::Context,
) -> Rect {
init_path(&path.segments, cr);
let mut is_square_cap = false;
if let Some(ref stroke) = path.stroke {
is_square_cap = stroke.linecap == usvg::LineCap::Square;
}
draw_path(&path.segments, is_square_cap, cr);
let bbox = utils::path_bbox(&path.segments, None, &usvg::Transform::default());
......@@ -37,25 +42,89 @@ pub fn draw(
bbox
}
pub fn init_path(
list: &[usvg::PathSegment],
fn draw_path(
segments: &[usvg::PathSegment],
is_square_cap: bool,
cr: &cairo::Context,
) {
for seg in list {
match *seg {
usvg::PathSegment::MoveTo { x, y } => {
cr.new_sub_path();
cr.move_to(x, y);
}
usvg::PathSegment::LineTo { x, y } => {
cr.line_to(x, y);
}
usvg::PathSegment::CurveTo { x1, y1, x2, y2, x, y } => {
cr.curve_to(x1, y1, x2, y2, x, y);
let mut i = 0;
loop {
let subpath = get_subpath(i, segments);
if subpath.is_empty() {
break;
}
draw_subpath(subpath, is_square_cap, cr);
i += subpath.len();
}
}
fn get_subpath(start: usize, segments: &[usvg::PathSegment]) -> &[usvg::PathSegment] {
let mut i = start;
while i < segments.len() {
match segments[i] {
usvg::PathSegment::MoveTo { .. } => {
if i != start {
break;
}
}
usvg::PathSegment::ClosePath => {
cr.close_path();
i += 1;
break;
}
_ => {}
}
i += 1;
}
&segments[start..i]
}
fn draw_subpath(
segments: &[usvg::PathSegment],
is_square_cap: bool,
cr: &cairo::Context,
) {
assert_ne!(segments.len(), 0);
// This is a workaround for a cairo bug(?).
//
// Buy the SVG spec, a zero length subpath with a square cap should be
// rendered as a square/rect, but it's not (at least on 1.14.12/1.15.12).
// And this is probably a bug, since round cap is rendered correctly.
let mut is_zero_path = false;
if is_square_cap {
if utils::path_length(segments).is_fuzzy_zero() {
is_zero_path = true;
}
}
if !is_zero_path {
for seg in segments {
match *seg {
usvg::PathSegment::MoveTo { x, y } => {
cr.new_sub_path();
cr.move_to(x, y);
}
usvg::PathSegment::LineTo { x, y } => {
cr.line_to(x, y);
}
usvg::PathSegment::CurveTo { x1, y1, x2, y2, x, y } => {
cr.curve_to(x1, y1, x2, y2, x, y);
}
usvg::PathSegment::ClosePath => {
cr.close_path();
}
}
}
} else {
if let usvg::PathSegment::MoveTo { x, y } = segments[0] {
// Draw zero length path.
let shift = 0.001; // Purely empirical.
cr.new_sub_path();
cr.move_to(x, y);
cr.line_to(x + shift, y);
}
}
}
......@@ -40,7 +40,7 @@ pub fn draw(
bbox
}
pub fn convert_path(
fn convert_path(
list: &[usvg::PathSegment],
rule: usvg::FillRule,
p_path: &mut qt::PainterPath,
......
......@@ -12,9 +12,10 @@ pub(crate) use usvg::{
};
pub use usvg::{
Line,
Point,
Size,
Rect,
Size,
};
......
......@@ -160,6 +160,69 @@ pub fn path_bbox(
(minx as f64, miny as f64, width as f64, height as f64).into()
}
/// Calculates path's length.
///
/// Length from the first segment to the first MoveTo, ClosePath or slice end.
pub fn path_length(segments: &[usvg::PathSegment]) -> f64 {
debug_assert!(!segments.is_empty());
use lyon_geom;
let (mut prev_x, mut prev_y) = {
if let usvg::PathSegment::MoveTo { x, y } = segments[0] {
(x as f32, y as f32)
} else {
panic!("first segment must be MoveTo");
}
};
let start_x = prev_x;
let start_y = prev_y;
let mut is_first_seg = true;
let mut length = 0.0f64;
for seg in segments {
match *seg {
usvg::PathSegment::MoveTo { .. } => {
if !is_first_seg {
break;
}
}
usvg::PathSegment::LineTo { x, y } => {
length += Line::new(prev_x as f64, prev_y as f64, x, y).length();
prev_x = x as f32;
prev_y = y as f32;
}
usvg::PathSegment::CurveTo { x1, y1, x2, y2, x, y } => {
let x = x as f32;
let y = y as f32;
let curve = lyon_geom::CubicBezierSegment {
from: lyon_geom::math::Point::new(prev_x, prev_y),
ctrl1: lyon_geom::math::Point::new(x1 as f32, y1 as f32),
ctrl2: lyon_geom::math::Point::new(x2 as f32, y2 as f32),
to: lyon_geom::math::Point::new(x, y),
};
length += curve.approximate_length(1.0) as f64;
prev_x = x;
prev_y = y;
}
usvg::PathSegment::ClosePath => {
length += Line::new(prev_x as f64, prev_y as f64,
start_x as f64, start_y as f64).length();
break;
}
}
is_first_seg = false;
}
length
}
/// Applies the transform to the path segments.
pub fn transform_path(segments: &mut [usvg::PathSegment], ts: &usvg::Transform) {
for seg in segments {
......
e-clipPath-019.svg
e-clipPath-020.svg
e-clipPath-029.svg
e-clipPath-031.svg
e-clipPath-032.svg
e-clipPath-033.svg
e-clipPath-034.svg
e-clipPath-036.svg
e-clipPath-037.svg
e-mask-022.svg
a-stroke-linecap-005.svg
e-clipPath-019.svg
e-clipPath-020.svg
e-clipPath-029.svg
e-clipPath-031.svg
e-clipPath-032.svg
e-clipPath-033.svg
e-clipPath-034.svg
e-clipPath-036.svg
e-clipPath-037.svg
e-mask-022.svg
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment