]> source.dussan.org Git - poi.git/commitdiff
[github-604] XDGF: add support for poly lines. Thanks to Dmitrii Komarov. This closes...
authorPJ Fanning <fanningpj@apache.org>
Wed, 6 Mar 2024 11:53:17 +0000 (11:53 +0000)
committerPJ Fanning <fanningpj@apache.org>
Wed, 6 Mar 2024 11:53:17 +0000 (11:53 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1916146 13f79535-47bb-0310-9956-ffa450edef68

poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/PolyLineTo.java
poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryTestUtils.java
poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestPolylineTo.java [new file with mode: 0644]

index 70d652bda9d810973e2faa4bda7f1465a1d717ba..495a7d3f39c04c78d13357b0c4e314eda5e7ea8d 100644 (file)
@@ -27,6 +27,9 @@ import com.microsoft.schemas.office.visio.x2012.main.RowType;
 
 public class PolyLineTo implements GeometryRow {
 
+    private static final String POLYLINE_FORMULA_PREFIX = "POLYLINE(";
+    private static final String POLYLINE_FORMULA_SUFFIX = ")";
+
     PolyLineTo _master;
 
     // The x-coordinate of the ending vertex of a polyline.
@@ -96,6 +99,39 @@ public class PolyLineTo implements GeometryRow {
     public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) {
         if (getDel())
             return;
-        throw new POIXMLException("Polyline support not implemented");
+
+        // A polyline formula: POLYLINE(xType, yType, x1, y1, x2, y2, ...)
+        String formula = getA().trim();
+        if (!formula.startsWith(POLYLINE_FORMULA_PREFIX) || !formula.endsWith(POLYLINE_FORMULA_SUFFIX)) {
+            throw new POIXMLException("Invalid POLYLINE formula: " + formula);
+        }
+
+        String[] components = formula
+                .substring(POLYLINE_FORMULA_PREFIX.length(), formula.length() - POLYLINE_FORMULA_SUFFIX.length())
+                .split(",");
+
+        if (components.length < 2) {
+            throw new POIXMLException("Invalid POLYLINE formula (not enough arguments): " + formula);
+        }
+
+        if (components.length % 2 != 0) {
+            throw new POIXMLException("Invalid POLYLINE formula -- need 2 + n*2 arguments, got " + components.length);
+        }
+
+        if (components.length > 2) {
+            // If xType is zero, the X coordinates are interpreted as relative coordinates
+            double xScale = Integer.parseInt(components[0].trim()) == 0 ? parent.getWidth() : 1.0;
+            // If yType is zero, the Y coordinates are interpreted as relative coordinates
+            double yScale = Integer.parseInt(components[1].trim()) == 0 ? parent.getHeight() : 1.0;
+
+            for (int i = 2; i < components.length - 1; i += 2) {
+                double x = Double.parseDouble(components[i].trim());
+                double y = Double.parseDouble(components[i + 1].trim());
+
+                path.lineTo(x * xScale, y * yScale);
+            }
+        }
+
+        path.lineTo(getX(), getY());
     }
 }
index 71444f8088d616805f5b23ccc6a260335015e1e1..236afe20a8179ccf4766960f4b3188f4b9eba59b 100644 (file)
@@ -19,7 +19,9 @@ package org.apache.poi.xdgf.usermodel.section.geometry;
 
 import com.microsoft.schemas.office.visio.x2012.main.CellType;
 import com.microsoft.schemas.office.visio.x2012.main.RowType;
+import com.microsoft.schemas.office.visio.x2012.main.ShapeSheetType;
 import org.apache.poi.util.LocaleUtil;
+import org.apache.poi.xdgf.usermodel.XDGFShape;
 import org.junit.jupiter.api.Assertions;
 
 import java.awt.geom.Path2D;
@@ -99,4 +101,19 @@ public final class GeometryTestUtils {
         }
     }
 
+    /**
+     * Mocks a shape for testing geometries with relative coordinates.
+     */
+    public static XDGFShape mockShape(double width, double height) {
+        ShapeSheetType shapeSheet = ShapeSheetType.Factory.newInstance();
+        CellType[] cells = {
+                createCell("Width", Double.toString(width)),
+                createCell("Height", Double.toString(height))
+        };
+        shapeSheet.setCellArray(cells);
+
+        // Parent page and document is not used during parsing. It's safe to leave them as nulls for mocking
+        return new XDGFShape(shapeSheet, null, null);
+    }
+
 }
diff --git a/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestPolylineTo.java b/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestPolylineTo.java
new file mode 100644 (file)
index 0000000..57eb00e
--- /dev/null
@@ -0,0 +1,109 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.xdgf.usermodel.section.geometry;
+
+import com.microsoft.schemas.office.visio.x2012.main.RowType;
+import org.apache.poi.ooxml.POIXMLException;
+import org.apache.poi.xdgf.usermodel.XDGFShape;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.awt.geom.Path2D;
+import java.util.HashMap;
+
+public class TestPolylineTo {
+
+    private static final double X0 = 0.0;
+    private static final double Y0 = 0.0;
+    private static final double X = 100.0;
+    private static final double Y = 100.0;
+
+    @ParameterizedTest
+    @ValueSource(strings = {
+            "POLYLINE(1, 1, 0.0, 50.0, 100.0, 50.0)",
+            "POLYLINE(1, 0, 0.0,  0.5, 100.0,  0.5)",
+            "POLYLINE(0, 1, 0.0, 50.0,   1.0, 50.0)",
+            "POLYLINE(0, 0, 0.0,  0.5,   1.0,  0.5)"
+    })
+    public void shouldAddMultipleLinesToPath(String formula) {
+        PolyLineTo polyLine = createPolyLine(formula);
+
+        XDGFShape parent = GeometryTestUtils.mockShape(X - X0, Y - Y0);
+
+        Path2D.Double actualPath = new Path2D.Double();
+        actualPath.moveTo(X0, Y0);
+
+        polyLine.addToPath(actualPath, parent);
+
+        Path2D expectedPath = new Path2D.Double();
+        expectedPath.moveTo(X0, Y0);
+        expectedPath.lineTo(0.0, 50.0);
+        expectedPath.lineTo(100.0, 50.0);
+        expectedPath.lineTo(X, Y);
+
+        GeometryTestUtils.assertPath(expectedPath, actualPath);
+    }
+
+    @Test
+    public void shouldAddSingleLineToPath() {
+        PolyLineTo polyLine = createPolyLine("POLYLINE(1, 1)");
+
+        XDGFShape parent = GeometryTestUtils.mockShape(X - X0, Y - Y0);
+
+        Path2D.Double actualPath = new Path2D.Double();
+        actualPath.moveTo(X0, Y0);
+
+        polyLine.addToPath(actualPath, parent);
+
+        Path2D expectedPath = new Path2D.Double();
+        expectedPath.moveTo(X0, Y0);
+        expectedPath.lineTo(X, Y);
+
+        GeometryTestUtils.assertPath(expectedPath, actualPath);
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {
+            "1, 1)",                 // Does not start with POLYLINE(
+            "POLYLINE(1, 1",         // Does not end with )
+            "POLYLINE()",            // Empty arguments
+            "POLYLINE(1)",           // Not enough arguments (less than two)
+            "POLYLINE(1, 1, 100.0)", // Odd number of arguments
+    })
+    public void shouldThrowExceptionWhenPolyLineFormulaIsIncorrect(String formula) {
+        PolyLineTo polyLine = createPolyLine(formula);
+
+        Path2D.Double path = new Path2D.Double();
+        Assertions.assertThrows(POIXMLException.class, () -> polyLine.addToPath(path, null));
+    }
+
+    private static PolyLineTo createPolyLine(String formula) {
+        RowType row = GeometryTestUtils.createRow(
+                0L,
+                new HashMap<String, Object>() {{
+                    put("X", X);
+                    put("Y", Y);
+                    put("A", formula);
+                }}
+        );
+        return new PolyLineTo(row);
+    }
+
+}