ShortenPathData takes a full pathdata string and returns a shortened version. The original string is overwritten. It parses all commands (M, A, Z, ...) and coordinates (numbers) and calls copyInstruction for each command.
(b []byte)
| 71 | // ShortenPathData takes a full pathdata string and returns a shortened version. The original string is overwritten. |
| 72 | // It parses all commands (M, A, Z, ...) and coordinates (numbers) and calls copyInstruction for each command. |
| 73 | func (p *PathData) ShortenPathData(b []byte) []byte { |
| 74 | if 100000 < len(b) { |
| 75 | // prevent extremely long paths for being too costly (OSS-Fuzz) |
| 76 | return b |
| 77 | } |
| 78 | |
| 79 | var cmd byte |
| 80 | p.x, p.y = 0.0, 0.0 |
| 81 | p.coords = p.coords[:0] |
| 82 | p.coordFloats = p.coordFloats[:0] |
| 83 | p.state = PathDataState{} |
| 84 | |
| 85 | j := 0 |
| 86 | for i := 0; i < len(b); i++ { |
| 87 | c := b[i] |
| 88 | if c == ' ' || c == ',' || c == '\n' || c == '\r' || c == '\t' { |
| 89 | continue |
| 90 | } else if pathCmds[c] && (cmd == 0 || cmd != c || c == 'M' || c == 'm') { // any command |
| 91 | if cmd != 0 { |
| 92 | j += p.copyInstruction(b[j:], cmd) |
| 93 | } |
| 94 | cmd = c |
| 95 | p.coords = p.coords[:0] |
| 96 | p.coordFloats = p.coordFloats[:0] |
| 97 | } else if (cmd == 'A' || cmd == 'a') && (len(p.coordFloats)%7 == 3 || len(p.coordFloats)%7 == 4) { |
| 98 | // boolean flags for arc command |
| 99 | if c == '1' { |
| 100 | p.coords = append(p.coords, b[i:i+1]) |
| 101 | p.coordFloats = append(p.coordFloats, 1.0) |
| 102 | } else if c == '0' { |
| 103 | p.coords = append(p.coords, b[i:i+1]) |
| 104 | p.coordFloats = append(p.coordFloats, 0.0) |
| 105 | } else { |
| 106 | cmd = 0 // bad format, don't minify |
| 107 | } |
| 108 | } else if n := parse.Number(b[i:]); n > 0 { |
| 109 | f, _ := strconv.ParseFloat(b[i : i+n]) |
| 110 | p.coords = append(p.coords, b[i:i+n]) |
| 111 | p.coordFloats = append(p.coordFloats, f) |
| 112 | i += n - 1 |
| 113 | } |
| 114 | } |
| 115 | if cmd == 0 { |
| 116 | return b |
| 117 | } |
| 118 | j += p.copyInstruction(b[j:], cmd) |
| 119 | return b[:j] |
| 120 | } |
| 121 | |
| 122 | // copyInstruction copies pathdata of a single command, but may be comprised of multiple sets for that command. For example, L takes two coordinates, but this function may process 2*N coordinates. Lowercase commands are relative commands, where the coordinates are relative to the previous point. Uppercase commands have absolute coordinates. |
| 123 | // We update p.x and p.y (the current coordinates) according to the commands given. For each set of coordinates we call shortenCurPosInstruction and shortenAltPosInstruction. The former just minifies the coordinates, the latter will inverse the lowercase/uppercase of the command, and see if the coordinates get smaller due to that. The shortest is chosen and copied to b, i.e. b is the destination and is not read from. |