From 8a1c9fc08c853498f9906b1296c30498a8c15b6c Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Wed, 16 Apr 2008 08:44:07 +0000 Subject: [PATCH] Misc improvements in Freeform shape git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@648624 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hslf/model/Freeform.java | 110 ++++++++++++++++-- .../apache/poi/hslf/model/TestFreeform.java | 80 +++++++++++++ 2 files changed, 180 insertions(+), 10 deletions(-) create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java b/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java index e34d651b3d..28e41f8b09 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java @@ -19,9 +19,11 @@ package org.apache.poi.hslf.model; import org.apache.poi.ddf.*; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogger; +import org.apache.poi.util.HexDump; import java.awt.geom.*; import java.util.ArrayList; +import java.util.Arrays; /** * A "Freeform" shape. @@ -33,6 +35,16 @@ import java.util.ArrayList; * @author Yegor Kozlov */ public class Freeform extends AutoShape { + + public static final byte[] SEGMENTINFO_MOVETO = new byte[]{0x00, 0x40}; + public static final byte[] SEGMENTINFO_LINETO = new byte[]{0x00, (byte)0xAC}; + public static final byte[] SEGMENTINFO_ESCAPE = new byte[]{0x01, 0x00}; + public static final byte[] SEGMENTINFO_ESCAPE2 = new byte[]{0x01, 0x20}; + public static final byte[] SEGMENTINFO_CUBICTO = new byte[]{0x00, (byte)0xAD}; + public static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; //OpenOffice inserts 0xB3 instead of 0xAD. + public static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60}; + public static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80}; + /** * Create a Freeform object and initialize it from the supplied Record container. * @@ -82,36 +94,37 @@ public class Freeform extends AutoShape { switch (type) { case PathIterator.SEG_MOVETO: pntInfo.add(new Point2D.Double(vals[0], vals[1])); - segInfo.add(new byte[]{0x00, 0x40}); + segInfo.add(SEGMENTINFO_MOVETO); break; case PathIterator.SEG_LINETO: pntInfo.add(new Point2D.Double(vals[0], vals[1])); - segInfo.add(new byte[]{0x00, (byte)0xAC}); - segInfo.add(new byte[]{0x01, 0x00 }); + segInfo.add(SEGMENTINFO_LINETO); + segInfo.add(SEGMENTINFO_ESCAPE); break; case PathIterator.SEG_CUBICTO: pntInfo.add(new Point2D.Double(vals[0], vals[1])); pntInfo.add(new Point2D.Double(vals[2], vals[3])); pntInfo.add(new Point2D.Double(vals[4], vals[5])); - segInfo.add(new byte[]{0x00, (byte)0xAD}); - segInfo.add(new byte[]{0x01, 0x20 }); + segInfo.add(SEGMENTINFO_CUBICTO); + segInfo.add(SEGMENTINFO_ESCAPE2); break; case PathIterator.SEG_QUADTO: + //TODO: figure out how to convert SEG_QUADTO into SEG_CUBICTO logger.log(POILogger.WARN, "SEG_QUADTO is not supported"); break; case PathIterator.SEG_CLOSE: pntInfo.add(pntInfo.get(0)); - segInfo.add(new byte[]{0x00, (byte)0xAC}); - segInfo.add(new byte[]{0x01, 0x00 }); - segInfo.add(new byte[]{0x00, (byte)0xAC}); - segInfo.add(new byte[]{0x01, (byte)0x60}); + segInfo.add(SEGMENTINFO_LINETO); + segInfo.add(SEGMENTINFO_ESCAPE); + segInfo.add(SEGMENTINFO_LINETO); + segInfo.add(SEGMENTINFO_CLOSE); isClosed = true; break; } it.next(); } - if(!isClosed) segInfo.add(new byte[]{0x00, (byte)0xAC}); + if(!isClosed) segInfo.add(SEGMENTINFO_LINETO); segInfo.add(new byte[]{0x00, (byte)0x80}); EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); @@ -147,4 +160,81 @@ public class Freeform extends AutoShape { setAnchor(bounds); } + + /** + * Gets the freeform path + * + * @return the freeform path + */ + public GeneralPath getPath(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4)); + + EscherArrayProperty verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000)); + if(verticesProp == null) verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES)); + + EscherArrayProperty segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000)); + if(segmentsProp == null) segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO)); + + //sanity check + if(verticesProp == null) { + logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES "); + return null; + } + if(segmentsProp == null) { + logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO "); + return null; + } + + Rectangle2D bounds = getAnchor2D(); + float right = (float)bounds.getX(); + float bottom = (float)bounds.getY(); + + GeneralPath path = new GeneralPath(); + int numPoints = verticesProp.getNumberOfElementsInArray(); + int numSegments = segmentsProp.getNumberOfElementsInArray(); + for (int i = 0, j = 0; i < numSegments && j < numPoints; i++) { + byte[] elem = segmentsProp.getElement(i); + if(Arrays.equals(elem, SEGMENTINFO_MOVETO)){ + byte[] p = verticesProp.getElement(j++); + short x = LittleEndian.getShort(p, 0); + short y = LittleEndian.getShort(p, 2); + path.moveTo( + ((float)x*POINT_DPI/MASTER_DPI + right), + ((float)y*POINT_DPI/MASTER_DPI + bottom)); + } else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){ + i++; + byte[] p1 = verticesProp.getElement(j++); + short x1 = LittleEndian.getShort(p1, 0); + short y1 = LittleEndian.getShort(p1, 2); + byte[] p2 = verticesProp.getElement(j++); + short x2 = LittleEndian.getShort(p2, 0); + short y2 = LittleEndian.getShort(p2, 2); + byte[] p3 = verticesProp.getElement(j++); + short x3 = LittleEndian.getShort(p3, 0); + short y3 = LittleEndian.getShort(p3, 2); + path.curveTo( + ((float)x1*POINT_DPI/MASTER_DPI + right), ((float)y1*POINT_DPI/MASTER_DPI + bottom), + ((float)x2*POINT_DPI/MASTER_DPI + right), ((float)y2*POINT_DPI/MASTER_DPI + bottom), + ((float)x3*POINT_DPI/MASTER_DPI + right), ((float)y3*POINT_DPI/MASTER_DPI + bottom)); + + } else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){ + i++; + byte[] pnext = segmentsProp.getElement(i); + if(Arrays.equals(pnext, SEGMENTINFO_ESCAPE)){ + if(j + 1 < numPoints){ + byte[] p = verticesProp.getElement(j++); + short x = LittleEndian.getShort(p, 0); + short y = LittleEndian.getShort(p, 2); + path.lineTo( + ((float)x*POINT_DPI/MASTER_DPI + right), ((float)y*POINT_DPI/MASTER_DPI + bottom)); + } + } else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){ + path.closePath(); + } + } + } + + return path; + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java new file mode 100755 index 0000000000..5e7e7cc4b7 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java @@ -0,0 +1,80 @@ +/* ==================================================================== + 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.hslf.model; + +import junit.framework.TestCase; +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.hslf.usermodel.RichTextRun; +import org.apache.poi.hslf.HSLFSlideShow; + +import java.awt.*; +import java.awt.Rectangle; +import java.awt.geom.*; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Test Freeform object. + * The Freeform shape is constructed from java.awt.GeneralPath. + * Check that the get/set path accessors are consistent. + * (TODO: verification of Bezier curves is more difficult due to rounding error. Figure out a test approach for that) + * + * @author Yegor Kozlov + */ +public class TestFreeform extends TestCase { + + public void testClosedPath() throws Exception { + + GeneralPath path1 = new GeneralPath(); + path1.moveTo(100, 100); + path1.lineTo(200, 100); + path1.lineTo(200, 200); + path1.lineTo(100, 200); + path1.closePath(); + + Freeform p = new Freeform(); + p.setPath(path1); + + GeneralPath path2 = p.getPath(); + assertTrue(new Area(path1).equals(new Area(path2))); + } + + public void testLine() throws Exception { + + GeneralPath path1 = new GeneralPath(new Line2D.Double(100, 100, 200, 100)); + + Freeform p = new Freeform(); + p.setPath(path1); + + GeneralPath path2 = p.getPath(); + assertTrue(new Area(path1).equals(new Area(path2))); + } + + public void testRectangle() throws Exception { + + GeneralPath path1 = new GeneralPath(new Rectangle2D.Double(100, 100, 200, 50)); + + Freeform p = new Freeform(); + p.setPath(path1); + + GeneralPath path2 = p.getPath(); + assertTrue(new Area(path1).equals(new Area(path2))); + } +} -- 2.39.5