1 use std::ops::Deref; 2 3 use super::{Command, Number, Parameters, Position}; 4 use crate::node::Value; 5 use crate::parser::{Error, Reader, Result}; 6 7 /// A [data][1] attribute. 8 /// 9 /// [1]: https://www.w3.org/TR/SVG/paths.html#PathData 10 #[derive(Clone, Debug, Default)] 11 pub struct Data(Vec<Command>); 12 13 struct Parser<'l> { 14 reader: Reader<'l>, 15 } 16 17 impl Data { 18 /// Create a data attribute. 19 #[inline] 20 pub fn new() -> Self { 21 Default::default() 22 } 23 24 /// Parse a data attribute. 25 #[inline] 26 pub fn parse(content: &str) -> Result<Self> { 27 Parser::new(content).process() 28 } 29 30 /// Add a command. 31 #[inline] 32 pub fn add(mut self, command: Command) -> Self { 33 self.0.push(command); 34 self 35 } 36 } 37 38 macro_rules! implement { 39 (@one #[$doc:meta] fn $method:ident($command:ident, $position:ident)) => ( 40 #[$doc] 41 pub fn $method<T>(mut self, parameters: T) -> Self 42 where 43 T: Into<Parameters>, 44 { 45 self.0.push(Command::$command(Position::$position, parameters.into())); 46 self 47 } 48 ); 49 (@one #[$doc:meta] fn $method:ident($command:ident)) => ( 50 #[$doc] 51 pub fn $method(mut self) -> Self { 52 self.0.push(Command::$command); 53 self 54 } 55 ); 56 ($(#[$doc:meta] fn $method:ident($($argument:tt)*))*) => ( 57 impl Data { 58 $(implement! { @one #[$doc] fn $method($($argument)*) })* 59 } 60 ); 61 } 62 63 implement! { 64 #[doc = "Add an absolute `Command::Move` command."] 65 fn move_to(Move, Absolute) 66 67 #[doc = "Add a relative `Command::Move` command."] 68 fn move_by(Move, Relative) 69 70 #[doc = "Add an absolute `Command::Line` command."] 71 fn line_to(Line, Absolute) 72 73 #[doc = "Add a relative `Command::Line` command."] 74 fn line_by(Line, Relative) 75 76 #[doc = "Add an absolute `Command::HorizontalLine` command."] 77 fn horizontal_line_to(HorizontalLine, Absolute) 78 79 #[doc = "Add a relative `Command::HorizontalLine` command."] 80 fn horizontal_line_by(HorizontalLine, Relative) 81 82 #[doc = "Add an absolute `Command::VerticalLine` command."] 83 fn vertical_line_to(VerticalLine, Absolute) 84 85 #[doc = "Add a relative `Command::VerticalLine` command."] 86 fn vertical_line_by(VerticalLine, Relative) 87 88 #[doc = "Add an absolute `Command::QuadraticCurve` command."] 89 fn quadratic_curve_to(QuadraticCurve, Absolute) 90 91 #[doc = "Add a relative `Command::QuadraticCurve` command."] 92 fn quadratic_curve_by(QuadraticCurve, Relative) 93 94 #[doc = "Add an absolute `Command::SmoothQuadraticCurve` command."] 95 fn smooth_quadratic_curve_to(SmoothQuadraticCurve, Absolute) 96 97 #[doc = "Add a relative `Command::SmoothQuadraticCurve` command."] 98 fn smooth_quadratic_curve_by(SmoothQuadraticCurve, Relative) 99 100 #[doc = "Add an absolute `Command::CubicCurve` command."] 101 fn cubic_curve_to(CubicCurve, Absolute) 102 103 #[doc = "Add a relative `Command::CubicCurve` command."] 104 fn cubic_curve_by(CubicCurve, Relative) 105 106 #[doc = "Add an absolute `Command::SmoothCubicCurve` command."] 107 fn smooth_cubic_curve_to(SmoothCubicCurve, Absolute) 108 109 #[doc = "Add a relative `Command::SmoothCubicCurve` command."] 110 fn smooth_cubic_curve_by(SmoothCubicCurve, Relative) 111 112 #[doc = "Add an absolute `Command::EllipticalArc` command."] 113 fn elliptical_arc_to(EllipticalArc, Absolute) 114 115 #[doc = "Add a relative `Command::EllipticalArc` command."] 116 fn elliptical_arc_by(EllipticalArc, Relative) 117 118 #[doc = "Add a `Command::Close` command."] 119 fn close(Close) 120 } 121 122 impl Deref for Data { 123 type Target = [Command]; 124 125 #[inline] 126 fn deref(&self) -> &Self::Target { 127 &self.0 128 } 129 } 130 131 impl From<Vec<Command>> for Data { 132 #[inline] 133 fn from(commands: Vec<Command>) -> Self { 134 Data(commands) 135 } 136 } 137 138 impl From<Data> for Vec<Command> { 139 #[inline] 140 fn from(Data(commands): Data) -> Self { 141 commands 142 } 143 } 144 145 impl From<Data> for Value { 146 #[inline] 147 fn from(Data(mut inner): Data) -> Self { 148 inner 149 .drain(..) 150 .map(String::from) 151 .collect::<Vec<_>>() 152 .join(" ") 153 .into() 154 } 155 } 156 157 macro_rules! raise( 158 ($parser:expr, $($argument:tt)*) => ( 159 return Err(Error::new($parser.reader.position(), format!($($argument)*))); 160 ); 161 ); 162 163 impl<'l> Parser<'l> { 164 #[inline] 165 fn new(content: &'l str) -> Self { 166 Parser { 167 reader: Reader::new(content), 168 } 169 } 170 171 fn process(&mut self) -> Result<Data> { 172 let mut commands = Vec::new(); 173 loop { 174 self.reader.consume_whitespace(); 175 match self.read_command()? { 176 Some(command) => commands.push(command), 177 _ => break, 178 } 179 } 180 Ok(Data(commands)) 181 } 182 183 fn read_command(&mut self) -> Result<Option<Command>> { 184 use super::Command::*; 185 use super::Position::*; 186 187 let name = match self.reader.next() { 188 Some(name) => match name { 189 'A'..='Z' | 'a'..='z' => name, 190 _ => raise!(self, "expected a path command"), 191 }, 192 _ => return Ok(None), 193 }; 194 self.reader.consume_whitespace(); 195 Ok(Some(match name { 196 'M' => Move(Absolute, self.read_parameters()?.into()), 197 'm' => Move(Relative, self.read_parameters()?.into()), 198 199 'L' => Line(Absolute, self.read_parameters()?.into()), 200 'l' => Line(Relative, self.read_parameters()?.into()), 201 202 'H' => HorizontalLine(Absolute, self.read_parameters()?.into()), 203 'h' => HorizontalLine(Relative, self.read_parameters()?.into()), 204 205 'V' => VerticalLine(Absolute, self.read_parameters()?.into()), 206 'v' => VerticalLine(Relative, self.read_parameters()?.into()), 207 208 'Q' => QuadraticCurve(Absolute, self.read_parameters()?.into()), 209 'q' => QuadraticCurve(Relative, self.read_parameters()?.into()), 210 211 'T' => SmoothQuadraticCurve(Absolute, self.read_parameters()?.into()), 212 't' => SmoothQuadraticCurve(Relative, self.read_parameters()?.into()), 213 214 'C' => CubicCurve(Absolute, self.read_parameters()?.into()), 215 'c' => CubicCurve(Relative, self.read_parameters()?.into()), 216 217 'S' => SmoothCubicCurve(Absolute, self.read_parameters()?.into()), 218 's' => SmoothCubicCurve(Relative, self.read_parameters()?.into()), 219 220 'A' => EllipticalArc(Absolute, self.read_parameters_elliptical_arc()?.into()), 221 'a' => EllipticalArc(Relative, self.read_parameters_elliptical_arc()?.into()), 222 223 'Z' | 'z' => Close, 224 225 _ => raise!(self, "found an unknown path command '{}'", name), 226 })) 227 } 228 229 fn read_parameters(&mut self) -> Result<Vec<Number>> { 230 let mut parameters = Vec::new(); 231 232 while let Some(number) = self.read_number()? { 233 parameters.push(number); 234 self.reader.consume_whitespace(); 235 self.reader.consume_char(','); 236 } 237 Ok(parameters) 238 } 239 240 fn read_parameters_elliptical_arc(&mut self) -> Result<Vec<Number>> { 241 let mut parameters = Vec::new(); 242 let mut index: usize = 1; 243 244 while let Some(number) = match index % 7 { 245 i if i == 4 || i == 5 => self.read_flag()?, 246 _ => self.read_number()?, 247 } { 248 index += 1; 249 parameters.push(number); 250 self.reader.consume_whitespace(); 251 self.reader.consume_char(','); 252 } 253 Ok(parameters) 254 } 255 256 fn read_flag(&mut self) -> Result<Option<Number>> { 257 self.reader.consume_whitespace(); 258 259 match self.reader.next() { 260 Some('0') => Ok(Some(0.0)), 261 Some('1') => Ok(Some(1.0)), 262 _ => raise!(self, "failed to parse a flag in an elliptical arc"), 263 } 264 } 265 266 pub fn read_number(&mut self) -> Result<Option<Number>> { 267 self.reader.consume_whitespace(); 268 let number = self 269 .reader 270 .capture(|reader| reader.consume_number()) 271 .and_then(|number| Some(String::from(number))); 272 match number { 273 Some(number) => match (&number).parse() { 274 Ok(number) => Ok(Some(number)), 275 _ => raise!(self, "failed to parse a number '{}'", number), 276 }, 277 _ => Ok(None), 278 } 279 } 280 } 281 282 #[cfg(test)] 283 mod tests { 284 use super::super::Command::*; 285 use super::super::Position::*; 286 use super::{Data, Parser}; 287 use crate::node::Value; 288 289 #[test] 290 fn data_into_value() { 291 let data = Data::new() 292 .line_to((1, 2)) 293 .cubic_curve_by((1, 2.5, 3, 4, 5, 6)) 294 .close(); 295 296 assert_eq!(Value::from(data).to_string(), "L1,2 c1,2.5,3,4,5,6 z"); 297 } 298 299 #[test] 300 fn data_parse() { 301 let data = Data::parse("M1,2 l3,4").unwrap(); 302 303 assert_eq!(data.len(), 2); 304 match data[0] { 305 Move(Absolute, ref parameters) => assert_eq!(¶meters[..], &[1.0, 2.0]), 306 _ => unreachable!(), 307 } 308 match data[1] { 309 Line(Relative, ref parameters) => assert_eq!(¶meters[..], &[3.0, 4.0]), 310 _ => unreachable!(), 311 } 312 } 313 314 #[test] 315 fn parser_read_command() { 316 macro_rules! run( 317 ($content:expr) => ({ 318 let mut parser = Parser::new($content); 319 parser.read_command().unwrap().unwrap() 320 }); 321 ); 322 323 macro_rules! test( 324 ($content:expr, $command:ident, $position:ident, $parameters:expr) => ( 325 match run!($content) { 326 $command($position, parameters) => assert_eq!(¶meters[..], $parameters), 327 _ => unreachable!(), 328 } 329 ); 330 ($content:expr, $command:ident) => ( 331 match run!($content) { 332 $command => {} 333 _ => unreachable!(), 334 } 335 ); 336 ); 337 338 test!("M4,2", Move, Absolute, &[4.0, 2.0]); 339 test!("m4,\n2", Move, Relative, &[4.0, 2.0]); 340 341 test!("L7, 8 9", Line, Absolute, &[7.0, 8.0, 9.0]); 342 test!("l 7,8 \n9", Line, Relative, &[7.0, 8.0, 9.0]); 343 344 test!("H\t6,9", HorizontalLine, Absolute, &[6.0, 9.0]); 345 test!("h6, \t9", HorizontalLine, Relative, &[6.0, 9.0]); 346 347 test!("V2.1,-3", VerticalLine, Absolute, &[2.1, -3.0]); 348 test!("v\n2.1 -3", VerticalLine, Relative, &[2.1, -3.0]); 349 350 test!("Q90.5 0", QuadraticCurve, Absolute, &[90.5, 0.0]); 351 test!("q90.5\n, 0", QuadraticCurve, Relative, &[90.5, 0.0]); 352 353 test!("T-1", SmoothQuadraticCurve, Absolute, &[-1.0]); 354 test!("t -1", SmoothQuadraticCurve, Relative, &[-1.0]); 355 356 test!("C0,1 0,2", CubicCurve, Absolute, &[0.0, 1.0, 0.0, 2.0]); 357 test!("c0 ,1 0, 2", CubicCurve, Relative, &[0.0, 1.0, 0.0, 2.0]); 358 359 test!("S42,0", SmoothCubicCurve, Absolute, &[42.0, 0.0]); 360 test!("s \t 42,0", SmoothCubicCurve, Relative, &[42.0, 0.0]); 361 362 test!( 363 "A1 1 2.6,0 0 0 -7", 364 EllipticalArc, 365 Absolute, 366 &[1.0, 1.0, 2.6, 0.0, 0.0, 0.0, -7.0] 367 ); 368 test!( 369 "a1 1 2.6,0 0 0 -7", 370 EllipticalArc, 371 Relative, 372 &[1.0, 1.0, 2.6, 0.0, 0.0, 0.0, -7.0] 373 ); 374 test!( 375 "a32 32 0 00.03-45.22", 376 EllipticalArc, 377 Relative, 378 &[32.0, 32.0, 0.0, 0.0, 0.0, 0.03, -45.22] 379 ); 380 test!( 381 "a48 48 0 1148-48", 382 EllipticalArc, 383 Relative, 384 &[48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0] 385 ); 386 test!( 387 "a82.6 82.6 0 0033.48-20.25", 388 EllipticalArc, 389 Relative, 390 &[82.6, 82.6, 0.0, 0.0, 0.0, 33.48, -20.25] 391 ); 392 test!( 393 "a82.45 82.45 0 00-20.24 33.47", 394 EllipticalArc, 395 Relative, 396 &[82.45, 82.45, 0.0, 0.0, 0.0, -20.24, 33.47] 397 ); 398 test!( 399 "a48 48 0 1148-48 48 48 0 01-48 48", 400 EllipticalArc, 401 Relative, 402 &[48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0, 48.0, 48.0, 0.0, 0.0, 1.0, -48.0, 48.0] 403 ); 404 test!( 405 "a48 48 0 1148-48 48 48 0 01-48 48 32 32 0 11.03-45.22", 406 EllipticalArc, 407 Relative, 408 &[ 409 48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0, 48.0, 48.0, 0.0, 0.0, 1.0, -48.0, 48.0, 410 32.0, 32.0, 0.0, 1.0, 1.0, 0.03, -45.22 411 ] 412 ); 413 test!( 414 "a2.51 2.51 0 01.25.32", 415 EllipticalArc, 416 Relative, 417 &[2.51, 2.51, 0.0, 0.0, 1.0, 0.25, 0.32] 418 ); 419 test!( 420 "a1 1 0 00.25.32", 421 EllipticalArc, 422 Relative, 423 &[1., 1., 0.0, 0.0, 0.0, 0.25, 0.32] 424 ); 425 test!( 426 "a1 1 0 000.25.32", 427 EllipticalArc, 428 Relative, 429 &[1., 1., 0.0, 0.0, 0.0, 0.25, 0.32] 430 ); 431 432 test!("Z", Close); 433 test!("z", Close); 434 } 435 436 #[test] 437 fn parser_read_parameters() { 438 macro_rules! test( 439 ($content:expr, $parameters:expr) => ({ 440 let mut parser = Parser::new($content); 441 let parameters = parser.read_parameters().unwrap(); 442 assert_eq!(¶meters[..], $parameters); 443 }); 444 ); 445 446 test!("1,2 3,4 5 6.7", &[1.0, 2.0, 3.0, 4.0, 5.0, 6.7]); 447 test!("4-3.1.3e2.4", &[4.0, -3.1, 0.3e2, 0.4]); 448 } 449 450 #[test] 451 fn parser_read_parameters_elliptical_arc() { 452 macro_rules! test( 453 ($content:expr, $parameters:expr) => ({ 454 let mut parser = Parser::new($content); 455 let parameters = parser.read_parameters_elliptical_arc().unwrap(); 456 assert_eq!(¶meters[..], $parameters); 457 }); 458 ); 459 460 test!( 461 "32 32 0 00.03-45.22", 462 &[32.0, 32.0, 0.0, 0.0, 0.0, 0.03, -45.22] 463 ); 464 test!("48 48 0 1148-48", &[48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0]); 465 } 466 467 #[test] 468 fn parser_read_number() { 469 macro_rules! test( 470 ($content:expr, $value:expr) => ({ 471 let mut parser = Parser::new($content); 472 assert_eq!(parser.read_number().unwrap().unwrap(), $value); 473 }); 474 ); 475 476 test!("0.30000000000000004", 0.3); 477 test!("1e-4", 1e-4); 478 test!("-1E2", -1e2); 479 test!("-0.00100E-002", -1e-5); 480 } 481 } 482