From 89201ea71488955661e519df65300b32ba669772 Mon Sep 17 00:00:00 2001 From: Harsh Date: Wed, 15 Oct 2025 01:04:47 +0530 Subject: [PATCH 1/2] Enhance PShapeSVG to support compact arc notation and add corresponding unit tests --- core/src/processing/core/PShapeSVG.java | 85 ++++++++++++++++--- .../processing/core/PShapeSVGPathTest.java | 79 +++++++++++++++++ 2 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 core/test/processing/core/PShapeSVGPathTest.java 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..91233e9ad4 --- /dev/null +++ b/core/test/processing/core/PShapeSVGPathTest.java @@ -0,0 +1,79 @@ +package processing.core; + +import org.junit.Assert; +import org.junit.Test; +import processing.data.XML; + +public class PShapeSVGPathTest { + + @Test + public void testCompactPathNotation() { + // Test the failing SVG from issue #1244 + 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 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() { + // Test various compact arc notations + String[] testCases = { + // Flags only concatenated (e.g., "01") + "", + // Flags and coordinate concatenated (e.g., "013") + "", + // Standard notation (should still work) + "" + }; + + try { + for (String svgContent : testCases) { + XML xml = XML.parse(svgContent); + PShapeSVG shape = new PShapeSVG(xml); + Assert.assertNotNull(shape); + } + } catch (Exception e) { + Assert.fail("Encountered exception " + e); + } + } + + @Test + public void testCompactArcWithNegativeCoordinates() { + // Test compact arc notation with negative coordinates + String svgContent = "" + + "" + + ""; + + try { + XML xml = XML.parse(svgContent); + PShapeSVG shape = new PShapeSVG(xml); + Assert.assertNotNull(shape); + } catch (Exception e) { + Assert.fail("Encountered exception " + e); + } + } +} From 11ca32b33384334747ba5195b731c09a1d57e65a Mon Sep 17 00:00:00 2001 From: Harsh Date: Wed, 15 Oct 2025 18:14:54 +0530 Subject: [PATCH 2/2] add suggested changes --- .../processing/core/PShapeSVGPathTest.java | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/core/test/processing/core/PShapeSVGPathTest.java b/core/test/processing/core/PShapeSVGPathTest.java index 91233e9ad4..298fb085de 100644 --- a/core/test/processing/core/PShapeSVGPathTest.java +++ b/core/test/processing/core/PShapeSVGPathTest.java @@ -8,7 +8,6 @@ public class PShapeSVGPathTest { @Test public void testCompactPathNotation() { - // Test the failing SVG from issue #1244 String svgContent = "" + "" + ""; @@ -17,6 +16,11 @@ public void testCompactPathNotation() { 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); } @@ -40,22 +44,47 @@ public void testWorkingPathNotation() { @Test public void testCompactArcNotationVariations() { - // Test various compact arc notations - String[] testCases = { - // Flags only concatenated (e.g., "01") - "", - // Flags and coordinate concatenated (e.g., "013") - "", - // Standard notation (should still work) - "" - }; + String svgContent1 = "" + + ""; try { - for (String svgContent : testCases) { - XML xml = XML.parse(svgContent); - PShapeSVG shape = new PShapeSVG(xml); - Assert.assertNotNull(shape); - } + 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); } @@ -63,15 +92,17 @@ public void testCompactArcNotationVariations() { @Test public void testCompactArcWithNegativeCoordinates() { - // Test compact arc notation with negative coordinates String svgContent = "" + - "" + - ""; + ""; try { XML xml = XML.parse(svgContent); PShapeSVG shape = new PShapeSVG(xml); - Assert.assertNotNull(shape); + 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); }