Parses the given path data string `d` into a sequence of SvgPathCommands, according to the SVG 1.1 grammar: https://www.w3.org/TR/SVG11/paths.html#PathDataBNF In case of invalid syntax, an error string is written to the optional output parameter `error`, and the returned SvgPathCommands is the path data up to (but not including) the first command segment with an invalid syntax, as per the SVG re
| 716 | // user. |
| 717 | // |
| 718 | std::vector<SvgPathCommand> parsePathData( |
| 719 | const std::string& d, std::string* error = nullptr) |
| 720 | { |
| 721 | using t = SvgPathCommandType; |
| 722 | using a = SvgPathArgumentType; |
| 723 | auto it = d.cbegin(); |
| 724 | auto end = d.cend(); |
| 725 | std::vector<SvgPathCommand> cmds; |
| 726 | readWhitespaces(it, end); |
| 727 | while (it != end) { |
| 728 | |
| 729 | // Read command type and relativeness |
| 730 | SvgPathCommandType type; |
| 731 | bool relative; |
| 732 | switch(*it) { |
| 733 | case 'Z': type = t::ClosePath; relative = false; break; |
| 734 | case 'M': type = t::MoveTo; relative = false; break; |
| 735 | case 'L': type = t::LineTo; relative = false; break; |
| 736 | case 'H': type = t::HLineTo; relative = false; break; |
| 737 | case 'V': type = t::VLineTo; relative = false; break; |
| 738 | case 'C': type = t::CCurveTo; relative = false; break; |
| 739 | case 'S': type = t::SCurveTo; relative = false; break; |
| 740 | case 'Q': type = t::QCurveTo; relative = false; break; |
| 741 | case 'T': type = t::TCurveTo; relative = false; break; |
| 742 | case 'A': type = t::ArcTo; relative = false; break; |
| 743 | |
| 744 | case 'z': type = t::ClosePath; relative = true; break; |
| 745 | case 'm': type = t::MoveTo; relative = true; break; |
| 746 | case 'l': type = t::LineTo; relative = true; break; |
| 747 | case 'h': type = t::HLineTo; relative = true; break; |
| 748 | case 'v': type = t::VLineTo; relative = true; break; |
| 749 | case 'c': type = t::CCurveTo; relative = true; break; |
| 750 | case 's': type = t::SCurveTo; relative = true; break; |
| 751 | case 'q': type = t::QCurveTo; relative = true; break; |
| 752 | case 't': type = t::TCurveTo; relative = true; break; |
| 753 | case 'a': type = t::ArcTo; relative = true; break; |
| 754 | |
| 755 | default: |
| 756 | // Unknown command character, or failed to parse first arg |
| 757 | // of non-first argtuple of previous command. |
| 758 | if (error) { |
| 759 | *error = "Failed to read command type or argument: "; |
| 760 | *error += *it; |
| 761 | } |
| 762 | return cmds; |
| 763 | } |
| 764 | |
| 765 | // Ensure first command is a MoveTo |
| 766 | if (cmds.empty() && type != t::MoveTo) { |
| 767 | if (error) { |
| 768 | *error = "First command must be 'M' or 'm'. Found '"; |
| 769 | *error += *it; |
| 770 | *error += "' instead."; |
| 771 | } |
| 772 | return cmds; |
| 773 | } |
| 774 | |
| 775 | // Advance iterator on success |
no test coverage detected