diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java
index f8aa3400fb..29d2dde10d 100644
--- a/core/src/processing/core/PShapeSVG.java
+++ b/core/src/processing/core/PShapeSVG.java
@@ -961,14 +961,36 @@ else if (lexState == LexState.EXP_HEAD) {
float rx = PApplet.parseFloat(pathTokens[i + 1]);
float ry = PApplet.parseFloat(pathTokens[i + 2]);
float angle = PApplet.parseFloat(pathTokens[i + 3]);
- boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
- boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
- float endX = PApplet.parseFloat(pathTokens[i + 6]);
- float endY = PApplet.parseFloat(pathTokens[i + 7]);
+ // In compact arc notation, flags and coordinates may be concatenated.
+ // e.g. "013" is parsed as large-arc=0, sweep=1, x=3
+ String token4 = pathTokens[i + 4];
+ boolean fa;
+ boolean fs;
+ float endX;
+ float endY;
+ int tokenOffset = 0;
+ if (isCompactArcNotation(token4)) {
+ fa = token4.charAt(0) == '1';
+ fs = token4.charAt(1) == '1';
+ if (token4.length() > 2) {
+ endX = PApplet.parseFloat(token4.substring(2));
+ endY = PApplet.parseFloat(pathTokens[i + 5]);
+ tokenOffset = -2;
+ } else {
+ endX = PApplet.parseFloat(pathTokens[i + 5]);
+ endY = PApplet.parseFloat(pathTokens[i + 6]);
+ tokenOffset = -1;
+ }
+ } else {
+ fa = PApplet.parseFloat(token4) != 0;
+ fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
+ endX = PApplet.parseFloat(pathTokens[i + 6]);
+ endY = PApplet.parseFloat(pathTokens[i + 7]);
+ }
parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
cx = endX;
cy = endY;
- i += 8;
+ i += 8 + tokenOffset;
prevCurve = true;
}
break;
@@ -978,14 +1000,34 @@ else if (lexState == LexState.EXP_HEAD) {
float rx = PApplet.parseFloat(pathTokens[i + 1]);
float ry = PApplet.parseFloat(pathTokens[i + 2]);
float angle = PApplet.parseFloat(pathTokens[i + 3]);
- boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
- boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
- float endX = cx + PApplet.parseFloat(pathTokens[i + 6]);
- float endY = cy + PApplet.parseFloat(pathTokens[i + 7]);
+ String token4 = pathTokens[i + 4];
+ boolean fa;
+ boolean fs;
+ float endX;
+ float endY;
+ int tokenOffset = 0;
+ if (isCompactArcNotation(token4)) {
+ fa = token4.charAt(0) == '1';
+ fs = token4.charAt(1) == '1';
+ if (token4.length() > 2) {
+ endX = cx + PApplet.parseFloat(token4.substring(2));
+ endY = cy + PApplet.parseFloat(pathTokens[i + 5]);
+ tokenOffset = -2;
+ } else {
+ endX = cx + PApplet.parseFloat(pathTokens[i + 5]);
+ endY = cy + PApplet.parseFloat(pathTokens[i + 6]);
+ tokenOffset = -1;
+ }
+ } else {
+ fa = PApplet.parseFloat(token4) != 0;
+ fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
+ endX = cx + PApplet.parseFloat(pathTokens[i + 6]);
+ endY = cy + PApplet.parseFloat(pathTokens[i + 7]);
+ }
parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
cx = endX;
cy = endY;
- i += 8;
+ i += 8 + tokenOffset;
prevCurve = true;
}
break;
@@ -1054,6 +1096,29 @@ private void parsePathMoveto(float px, float py) {
}
+ /**
+ * Checks if a token represents compact arc notation where flags and coordinates
+ * are concatenated (e.g., "013" for large-arc=0, sweep=1, x=3).
+ *
+ * @param token the token to check
+ * @return true if the token is in compact arc notation format
+ */
+ private boolean isCompactArcNotation(String token) {
+ if (token == null) {
+ return false;
+ }
+ return token.length() > 1 &&
+ (token.charAt(0) == '0' || token.charAt(0) == '1') &&
+ (token.charAt(1) == '0' || token.charAt(1) == '1') &&
+ (token.length() == 2 ||
+ (token.length() > 2 && (
+ Character.isDigit(token.charAt(2)) ||
+ token.charAt(2) == '+' ||
+ token.charAt(2) == '-' ||
+ token.charAt(2) == '.')));
+ }
+
+
private void parsePathLineto(float px, float py) {
parsePathCode(VERTEX);
parsePathVertex(px, py);
diff --git a/core/test/processing/core/PShapeSVGPathTest.java b/core/test/processing/core/PShapeSVGPathTest.java
new file mode 100644
index 0000000000..298fb085de
--- /dev/null
+++ b/core/test/processing/core/PShapeSVGPathTest.java
@@ -0,0 +1,110 @@
+package processing.core;
+
+import org.junit.Assert;
+import org.junit.Test;
+import processing.data.XML;
+
+public class PShapeSVGPathTest {
+
+ @Test
+ public void testCompactPathNotation() {
+ String svgContent = "";
+
+ try {
+ XML xml = XML.parse(svgContent);
+ PShapeSVG shape = new PShapeSVG(xml);
+ Assert.assertNotNull(shape);
+ Assert.assertTrue(shape.getChildCount() > 0);
+
+ PShape path = shape.getChild(0);
+ Assert.assertNotNull(path);
+ Assert.assertTrue(path.getVertexCount() > 5);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+
+ @Test
+ public void testWorkingPathNotation() {
+ // Test the working SVG (with explicit decimal points)
+ String svgContent = "";
+
+ try {
+ XML xml = XML.parse(svgContent);
+ PShapeSVG shape = new PShapeSVG(xml);
+ Assert.assertNotNull(shape);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+
+ @Test
+ public void testCompactArcNotationVariations() {
+ String svgContent1 = "";
+
+ try {
+ XML xml = XML.parse(svgContent1);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(3.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(50.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+
+ String svgContent2 = "";
+
+ try {
+ XML xml = XML.parse(svgContent2);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(10.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(50.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+
+ String svgContent3 = "";
+
+ try {
+ XML xml = XML.parse(svgContent3);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(10.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(50.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+
+ @Test
+ public void testCompactArcWithNegativeCoordinates() {
+ String svgContent = "";
+
+ try {
+ XML xml = XML.parse(svgContent);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(40.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(70.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+}