]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924...
authorNick Burch <nick@apache.org>
Wed, 23 Apr 2008 16:49:18 +0000 (16:49 +0000)
committerNick Burch <nick@apache.org>
Wed, 23 Apr 2008 16:49:18 +0000 (16:49 +0000)
https://svn.apache.org:443/repos/asf/poi/trunk

........
  r648589 | yegor | 2008-04-16 08:47:16 +0100 (Wed, 16 Apr 2008) | 1 line

  bug #41071 is fixed in trunk. Added a unit test and resolved.
........
  r648623 | yegor | 2008-04-16 09:43:08 +0100 (Wed, 16 Apr 2008) | 1 line

  Rich text in HSSFTextbox must have at least one format run. Make sure it is so and apply th default fopnt if no formats were applied.
........
  r648624 | yegor | 2008-04-16 09:44:07 +0100 (Wed, 16 Apr 2008) | 1 line

  Misc improvements in Freeform shape
........
  r648674 | yegor | 2008-04-16 12:57:15 +0100 (Wed, 16 Apr 2008) | 1 line

  Support for getting OLE object data from slide show
........
  r649142 | yegor | 2008-04-17 16:06:01 +0100 (Thu, 17 Apr 2008) | 1 line

  added a unit test and closed bug #28774
........
  r649143 | yegor | 2008-04-17 16:08:03 +0100 (Thu, 17 Apr 2008) | 1 line

  initial support for rendering powerpoint slides into images
........
  r649145 | yegor | 2008-04-17 16:09:37 +0100 (Thu, 17 Apr 2008) | 1 line

  updated the list of changes
........
  r649557 | yegor | 2008-04-18 15:57:07 +0100 (Fri, 18 Apr 2008) | 1 line

  improved rendering of text
........
  r649796 | yegor | 2008-04-19 12:09:59 +0100 (Sat, 19 Apr 2008) | 1 line

  Support for getting embedded sounds from slide show
........
  r649797 | yegor | 2008-04-19 12:16:53 +0100 (Sat, 19 Apr 2008) | 1 line

  properly set shapeId for new shapes
........
  r649798 | yegor | 2008-04-19 12:17:37 +0100 (Sat, 19 Apr 2008) | 1 line

  misc improvements in slide rendering
........
  r649800 | yegor | 2008-04-19 12:52:36 +0100 (Sat, 19 Apr 2008) | 1 line

  updated the docs
........
  r649911 | yegor | 2008-04-20 12:17:48 +0100 (Sun, 20 Apr 2008) | 1 line

  more improvements in slide rendering
........
  r649914 | yegor | 2008-04-20 12:58:08 +0100 (Sun, 20 Apr 2008) | 1 line

  set version.id=3.0.3-beta1
........
  r650129 | yegor | 2008-04-21 13:51:47 +0100 (Mon, 21 Apr 2008) | 1 line

  more improvements in slide rendering
........
  r650130 | yegor | 2008-04-21 13:52:23 +0100 (Mon, 21 Apr 2008) | 1 line

  a couple of HSLF examples
........
  r650133 | yegor | 2008-04-21 14:10:33 +0100 (Mon, 21 Apr 2008) | 1 line

  update current version to 3.1-beta1
........
  r650138 | yegor | 2008-04-21 14:29:59 +0100 (Mon, 21 Apr 2008) | 1 line

  unfinished release guide. It would be nice to have a html version.
........
  r650139 | yegor | 2008-04-21 14:31:53 +0100 (Mon, 21 Apr 2008) | 1 line

  unfinished release guide. It would be nice to have a html version.
........

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@650938 13f79535-47bb-0310-9956-ffa450edef68

63 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/index.xml
src/documentation/content/xdocs/poifs/embeded.xml
src/documentation/content/xdocs/slideshow/how-to-shapes.xml
src/documentation/content/xdocs/slideshow/index.xml
src/documentation/content/xdocs/status.xml
src/documentation/release-guide.txt [new file with mode: 0755]
src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java
src/scratchpad/examples/src/org/apache/poi/hslf/examples/DataExtraction.java [new file with mode: 0755]
src/scratchpad/examples/src/org/apache/poi/hslf/examples/Graphics2DDemo.java [new file with mode: 0755]
src/scratchpad/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java
src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/Background.java
src/scratchpad/src/org/apache/poi/hslf/model/Fill.java
src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java
src/scratchpad/src/org/apache/poi/hslf/model/Line.java
src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java
src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java
src/scratchpad/src/org/apache/poi/hslf/model/Picture.java
src/scratchpad/src/org/apache/poi/hslf/model/Shape.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeOutline.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/ShapePainter.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
src/scratchpad/src/org/apache/poi/hslf/model/Slide.java
src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java
src/scratchpad/src/org/apache/poi/hslf/model/Table.java
src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java
src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java
src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java
src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java
src/scratchpad/src/org/apache/poi/hslf/record/PersistRecord.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
src/scratchpad/src/org/apache/poi/hslf/record/Sound.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/SoundCollection.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/ObjectData.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SoundData.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/data/41071.ppt [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/data/ringin.wav [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/data/sound.ppt [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java
src/scratchpad/testcases/org/apache/poi/hslf/record/TestSound.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestSlideOrdering.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestSoundData.java [new file with mode: 0755]
src/testcases/org/apache/poi/hssf/data/28774.xls [new file with mode: 0755]
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFTextbox.java

index 0fb5cac07c7924f258459ddba36e5b3d67cee8e4..d0d73c8d8ab06dd39b15797c829274417662ec49 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.0.3-beta1" date="2008-04-??">
+        <release version="3.1-beta1" date="2008-04-??">
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting embedded sounds from slide show </action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Initial support for rendering slides into images</action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting OLE object data from slide show </action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Implemented more methods in PPGraphics2D</action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Added Freeform shape which can contain both lines and Bezier curves</action>
+           <action dev="POI-DEVELOPERS" type="fix">41071 - Improved text extraction in HSLF</action>
            <action dev="POI-DEVELOPERS" type="add">30311 - Conditional Formatting - improved API, added HSSFSheetConditionalFormatting</action>
            <action dev="POI-DEVELOPERS" type="fix">Update the formula parser code to use a HSSFWorkbook, rather than the low level model.Workbook, to make things cleaner and make supporting XSSF formulas in future much easier</action>
            <action dev="POI-DEVELOPERS" type="fix">Fix the logger used by POIFSFileSystem, so that commons-logging isn't required when not used</action>
index 5d5067c8a988ecfbc9ea3d84cd7bd90e30d1dd29..e6f136652b3cd9107476681358649a2c5b264418 100644 (file)
         </section>
         <section><title>HSLF for PowerPoint Documents</title>
        <p>HSLF is our port of the Microsoft PowerPoint 97(-2003) file format to pure
-         Java. It supports read and write capabilities of some, but not yet all
-      of the core records. Please see <link
+         Java. It supports read and write capabilities. Please see <link
            href="./slideshow/index.html">the HSLF project page for more
            information</link>.</p>
         </section>
index d888e2ed534c00574ddf0523f290a6d1b50728d8..a4620f5a94dea4f8e2be6580652a143d87379e68 100644 (file)
@@ -61,9 +61,9 @@
        <section><title>Files embeded in PowerPoint</title>
          <p>PowerPoint does not normally store embeded files
          in the OLE2 layer. Instead, they are held within records
-         of the main PowerPoint file. To get at them, you need to
-         find the appropriate data within the PowerPoint stream,
-         and work from that.</p>
+         of the main PowerPoint file. 
+         <br/>See the <link href="./../hslf/how-to-shapes.html#OLE">HSLF Tutorial</link> 
+         for how to retrieve embedded OLE objects from a presentation</p>
        </section>
     </section>
 
index 36e4a1138733b8e94c2f9ed89d7be8a90278c5f0..7959eedaf60539eeb30a5f00df5d1b18ff3cd8fe 100644 (file)
                     <li><link href="#Bullets">How to create bulleted lists</link></li>
                     <li><link href="#Hyperlinks">Hyperlinks</link></li>
                     <li><link href="#Tables">Tables</link></li>
+                    <li><link href="#RemoveShape">How to remove shapes</link></li>
+                    <li><link href="#OLE">How to retrieve embedded OLE objects</link></li>
+                    <li><link href="#Sound">How to retrieve embedded sounds</link></li>
+                    <li><link href="#Freeform">How to create shapes of arbitrary geometry</link></li>
+                    <li><link href="#Graphics2D">Shapes and Graphics2D</link></li>
+                    <li><link href="#Render">How to convert slides into images</link></li>
                 </ul>
             </section>
             <section><title>Features</title>
                 </section>
                 <anchor id="GetShapes"/>
                 <section><title>How to get shapes contained in a particular slide</title>
-                  <p>The superclass of all shapes in HSLF is the Shape class - the elemental object that composes a drawing.
-                    The following pictute shows the class tree of HSLF shapes:
-                  </p>
-                  <p>
-                         <img src="images/hslf_shapes.gif" alt="Class Tree of HSLF Shapes" width="611" height="412"/>
-                  </p>
                   <p>
-                    The following fragment demonstrates how to iterate over shapes for each slide.
+                    The following code demonstrates how to iterate over shapes for each slide.
                   </p>
                     <source>
   SlideShow ppt = new SlideShow(new HSLFSlideShow("slideshow.ppt"));
                     </source>
                 </section>
                   
-            </section>
+                <anchor id="RemoveShape"/>
+                <section><title>How to remove shapes from a slide</title>
+                  <source>
+
+        Shape[] shape = slide.getShapes();
+        for (int i = 0; i &lt; shape.length; i++) {
+    
+            //remove the shape
+            boolean ok = slide.removeShape(shape[i]);
+            if(ok){
+              //the shape was removed. Do something.
+            }
+        }
+                    </source>
+                  </section>
+                <anchor id="OLE"/>
+                <section><title>How to retrieve embedded OLE objects</title>
+                  <source>
+
+        Shape[] shape = slide.getShapes();
+        for (int i = 0; i &lt; shape.length; i++) {
+            if (shape[i] instanceof OLEShape) {
+                OLEShape ole = (OLEShape) shape[i];
+                ObjectData data = ole.getObjectData();
+                String name = ole.getInstanceName();
+                if ("Worksheet".equals(name)) {
+                    HSSFWorkbook wb = new HSSFWorkbook(data.getData());
+                } else if ("Document".equals(name)) {
+                    HWPFDocument doc = new HWPFDocument(data.getData());
+                }
+            }
+        }
+                    </source>
+                  </section>
+
+                <anchor id="Sound"/>
+                <section><title>How to retrieve embedded sounds</title>
+                  <source>
+
+        FileInputStream is = new FileInputStream(args[0]);
+        SlideShow ppt = new SlideShow(is);
+        is.close();
+
+        SoundData[] sound = ppt.getSoundData();
+        for (int i = 0; i &lt; sound.length; i++) {
+            //save *WAV sounds on disk
+            if(sound[i].getSoundType().equals(".WAV")){
+                FileOutputStream out = new FileOutputStream(sound[i].getSoundName());
+                out.write(sound[i].getData());
+                out.close();
+            }
+        }
+                    </source>
+                  </section>
+                  
+                <anchor id="Freeform"/>
+                <section><title>How to create shapes of arbitrary geometry</title>
+                  <source>
+
+        SlideShow ppt = new SlideShow();
+        Slide slide = ppt.createSlide();
+
+        java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
+        path.moveTo(100, 100);
+        path.lineTo(200, 100);
+        path.curveTo(50, 45, 134, 22, 78, 133);
+        path.curveTo(10, 45, 134, 56, 78, 100);
+        path.lineTo(100, 200);
+        path.closePath();
+        
+        Freeform shape = new Freeform();
+        shape.setPath(path);
+        slide.addShape(shape);
+                    </source>
+                  </section>
+
+                <anchor id="Graphics2D"/>
+                <section><title>How to draw into a slide using Graphics2D</title>
+                  <warning>
+                   Current implementation of the PowerPoint Graphics2D driver is not fully compliant with the java.awt.Graphics2D specification.
+                   Some features like clipping, drawing of images are not yet supported. 
+                  </warning>
+                  <source>
+        SlideShow ppt = new SlideShow();
+        Slide slide = ppt.createSlide();
+
+        //draw a simple bar graph
+        //bar chart data. The first value is the bar color, the second is the width
+        Object[] def = new Object[]{
+            Color.yellow, new Integer(100),
+            Color.green, new Integer(150),
+            Color.gray, new Integer(75),
+            Color.red, new Integer(200),
+        };
+
+        //all objects are drawn into a shape group so we need to create one
+
+        ShapeGroup group = new ShapeGroup();
+        //define position of the drawing in the slide
+        Rectangle bounds = new java.awt.Rectangle(200, 100, 350, 300);
+        //if you want to draw in the entire slide area then define the anchor as follows:
+        //Dimension pgsize = ppt.getPageSize();
+        //java.awt.Rectangle bounds = new java.awt.Rectangle(0, 0, pgsize.width, pgsize.height);
+
+        group.setAnchor(bounds);
+        slide.addShape(group);
+
+        //draw a simple bar chart
+        Graphics2D graphics = new PPGraphics2D(group);
+        int x = bounds.x + 50, y = bounds.y + 50;
+        graphics.setFont(new Font("Arial", Font.BOLD, 10));
+        for (int i = 0, idx = 1; i &lt; def.length; i+=2, idx++) {
+            graphics.setColor(Color.black);
+            int width = ((Integer)def[i+1]).intValue();
+            graphics.drawString("Q" + idx, x-20, y+20);
+            graphics.drawString(width + "%", x + width + 10, y + 20);
+            graphics.setColor((Color)def[i]);
+            graphics.fill(new Rectangle(x, y, width, 30));
+            y += 40;
+        }
+        graphics.setColor(Color.black);
+        graphics.setFont(new Font("Arial", Font.BOLD, 14));
+        graphics.draw(bounds);
+        graphics.drawString("Performance", x + 70, y + 40);
+
+        FileOutputStream out = new FileOutputStream("hslf-graphics2d.ppt");
+        ppt.write(out);
+        out.close();
+
+                   </source>
+                  </section>
+
+                <anchor id="Render"/>
+                <section><title>Export PowerPoint slides into java.awt.Graphics2D</title>
+                  <p>
+                    HSLF provides a way to export slides into images. You can capture slides into java.awt.Graphics2D object (or any other) 
+                    and serialize it into a PNG or JPEG format. Please note, although HSLF attempts to render slides as close to PowerPoint as possible, 
+                    the output may look differently from PowerPoint due to the following reasons: 
+                  </p>
+            <ul>
+              <li>Java2D renders fonts differently vs PowerPoint. There are always some differences in the way the font glyphs are painted</li>   
+              <li>HSLF uses java.awt.font.LineBreakMeasurer to break text into lines. PowerPoint may do it in a different way.</li>
+              <li>If a font from the presentation is not avaiable, then the JDK default font will be used.</li>
+            </ul>
+            <p>
+            Current Limitations:
+            </p>
+            <ul>
+              <li>Some types of shapes are not yet supported (WordArt, complex auto-shapes)</li>
+              <li>Only Bitmap images (PNG, JPEG, DIB) can be rendered in Java</li>  
+            </ul>
+                  <source>
+        FileInputStream is = new FileInputStream("slideshow.ppt");
+        SlideShow ppt = new SlideShow(is);
+        is.close();
+        
+        Dimension pgsize = ppt.getPageSize();
+
+        Slide[] slide = ppt.getSlides();
+        for (int i = 0; i &lt; slide.length; i++) {
+
+            BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB);
+            Graphics2D graphics = img.createGraphics();
+            //clear the drawing area
+            graphics.setPaint(Color.white);
+            graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
+
+            //render
+            slide[i].draw(graphics);
+
+            //save the output
+            FileOutputStream out = new FileOutputStream("slide-"  + (i+1) + ".png");
+            javax.imageio.ImageIO.write(img, "png", out);
+            out.close();
+        }
+
+                  </source>
+                  </section>
+                  
+                </section>
         </section>
     </body>
 </document>
index 438aab3f13cc505b44f3eb8f3e3143545468fb07..a4718a6d67e3879b600a76425351cd260696cadd 100755 (executable)
@@ -26,6 +26,7 @@
         <authors>
             <person name="Avik Sengupta" email="avik at apache dot org"/>
             <person name="Nick Burch" email="nick at apache dot org"/>
+            <person name="Yegor Kozlov" email="yegor at apache dot org"/>
         </authors>
     </header>
 
                        Powerpoint '97(-2007) file format. It <em>does not</em> support
                        the new PowerPoint 2007 .pptx file format, which is not OLE2 
                        based.</p>
-            <p>HSLF provides a way to read powerpoint presentations, and extract text from it.
-            It also provides some (currently limited) edit capabilities.
+    <p>HSLF provides a way to read, create or modify PowerPoint  presentations. In particular, it provides:
             </p>
+            <ul>
+                <li>api for data extraction (text, pictures, embedded objects, sounds)</li>
+                <li>usermodel api for creating, reading and modifying ppt files</li>
+            </ul>
             <note> 
                 This code currently lives the 
                 <link href="http://svn.apache.org/viewcvs.cgi/poi/trunk/src/scratchpad/">scratchpad area</link> 
index cef12de4c80ca9ef148c9c8aef24e4a067a2c9a1..8a9b8e1208e0766fdefc42fb4ea3ff470855b63a 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.0.3-beta1" date="2008-04-??">
+        <release version="3.1-beta1" date="2008-04-??">
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting embedded sounds from slide show </action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Initial support for rendering slides into images</action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting OLE object data from slide show </action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Implemented more methods in PPGraphics2D</action>
+           <action dev="POI-DEVELOPERS" type="add">HSLF: Added Freeform shape which can contain both lines and Bezier curves</action>
+           <action dev="POI-DEVELOPERS" type="fix">41071 - Improved text extraction in HSLF</action>
            <action dev="POI-DEVELOPERS" type="add">30311 - Conditional Formatting - improved API, added HSSFSheetConditionalFormatting</action>
            <action dev="POI-DEVELOPERS" type="fix">Update the formula parser code to use a HSSFWorkbook, rather than the low level model.Workbook, to make things cleaner and make supporting XSSF formulas in future much easier</action>
            <action dev="POI-DEVELOPERS" type="fix">Fix the logger used by POIFSFileSystem, so that commons-logging isn't required when not used</action>
diff --git a/src/documentation/release-guide.txt b/src/documentation/release-guide.txt
new file mode 100755 (executable)
index 0000000..8a38150
--- /dev/null
@@ -0,0 +1,135 @@
+POI Release Guide\r
+\r
+\r
+(I) Prerequisites\r
+\r
+   1. You should read the <a href="http://apache.org/dev/release.html">Apache Release FAQ</a>\r
+   2. You must have shell access to people.apache.org\r
+   3. Release manager must have his public key appended to the KEYS file checked in to SVN and the key published on one of the public key servers.\r
+      More info can be found here: <a href="http://www.apache.org/dev/release-signing.html">http://www.apache.org/dev/release-signing.html</a>\r
+   4. You must have <a href="java.sun.com">JDK 1.4 / 1.5</a>\r
+   5. You must have the following utilities installed on your local machine and available in your path:\r
+          * <a href="www.openssh.com">ssh</a>\r
+          * <a href="www.gnupg.org">gnupg</a>\r
+          * <a href="www.openssl.org">openssl</a>\r
+      For Windows users, install Cygwin and make sure you have the above utilities\r
+   6. The POI build system requires two components to perform a build\r
+          * <a href="ant.apache.org">Ant</a>  \r
+          * <a href="http://forrest.apache.org/">Forrest</a>. \r
+  POI 3.0.2 and 3.1 were built using Ant 1.6.2 and Forrest 0.5\r
+\r
+(II) Making release artefacts\r
+  1. Update version id in build.xml. \r
+  2. Tag current version. Include the current revision number in the comment\r
+\r
+{code}\r
+$ svn cp https://svn.apache.org/repos/asf/poi/trunk \\r
+https://svn.apache.org/repos/asf/poi/tags/$TAG \\r
+-m "tag r649911 as 3.1-beta1"\r
+{code}\r
+\r
+where $TAG is the release tag, for example, REL_3_1_BETA1\r
+\r
+  3. Checkout the tagged version\r
+{code}\r
+cd tags\r
+svn checkout https://svn.apache.org/repos/asf/poi/tags/TAG\r
+{code}\r
+\r
+  4. Merge (if required)\r
+\r
+{code}\r
+cd $TAG\r
+$ svn merge https://svn.apache.org/repos/asf/poi/tags/TAG \\r
+https://svn.apache.org/repos/asf/poi/trunk\r
+{code}\r
+\r
+  5. Start a new section in sites.xml and status.xml. \r
+\r
+  6. Build as if the vote had passed. The buid date must be +7 days from current.\r
+{code}\r
+ant build\r
+{code}\r
+After build you should have the following files in the build/dist:\r
+\r
+{code}\r
+poi-$TAG-$DATE.jar\r
+poi-bin-$TAG-$DATE.tar.gz\r
+poi-bin-$TAG-$DATE.zip\r
+poi-contrib-$TAG-$DATE.jar\r
+poi-scratchpad-$TAG-$DATE.jar\r
+poi-src-$TAG-$DATE.tar.gz\r
+poi-src-$TAG-$DATE.zip\r
+{code}\r
+\r
+where $TAG is the release tag specified in build.xml in the version.id property, $DATE is the release date (typically +7 days from the actual build date). \r
+  7. Build Mavn POM files\r
+{code}\r
+ant maven-dist\r
+{code}\r
+\r
+  8. Signing the release artifacts:\r
+{code}\r
+cd build/dist\r
+for i in *.zip ; do \r
+  gpg --armor --output $i.asc --detach-sig $i; \r
+done\r
+for i in *.gz ; do \r
+  gpg --armor --output $i.asc --detach-sig $i; \r
+done\r
+{code}\r
+\r
+Verify the signatures:\r
+\r
+{code}\r
+gpg --multifile --verify *.asc\r
+{code}\r
+\r
+   9. Create MD5 checksums for all artifacts to be published:\r
+\r
+{code}\r
+for i in *.zip ; do \r
+  openssl md5 < $i > $i.md5\r
+done\r
+for i in *.gz ; do \r
+  openssl md5 < $i > $i.md5\r
+done\r
+{code}\r
+\r
+   10. Upload to your area at people.apache.org.\r
+There should be two directories:\r
+main\r
+maven\r
+\r
+Make sure that the all files have read permission. \r
+\r
+ (III) After the vote:\r
+\r
+Log-in on people.apache.org\r
+\r
+1. Go to ~/POI-3.1-BETA1\r
+\r
+cd ~/POI-3.1-BETA1/main\r
+\r
+BETA and ALPHA releases:\r
+cp *-src-* /www/www.apache.org/dist/poi/dev/src\r
+cp *-bin-* /www/www.apache.org/dist/poi/dev/bin\r
+\r
+FINAL release:\r
+cp *-src-* /www/www.apache.org/dist/poi/release/src\r
+cp *-bin-* /www/www.apache.org/dist/poi/release/bin\r
+\r
+cd ~/POI-3.1-BETA1/maven\r
+\r
+cp -r org.apache.poi /www/people.apache.org/repo/m1-ibiblio-rsync-repository/\r
+cp -r poi/poms      /www/people.apache.org/repo/m1-ibiblio-rsync-repository/poi\r
+\r
+\r
+2. Make sure that the files are owned by the unix group apcvs and that they are writable by this group. \r
+\r
+3. Wait for the distributions to appear on your favourite mirror\r
+\r
+4. Send announcements:\r
+ - to poi-user and poi-dev lists\r
+ - send announcements to announcement@apache.org, announcements@jakarta.apache.org\r
+\r
index 48816131a4a581d2294d9ce2ceef6bf94ec27c01..34a5a15841f42c4886f751cb4b73e2742acae17d 100644 (file)
@@ -83,7 +83,12 @@ public class HSSFTextbox
      */
     public void setString( RichTextString string )
     {
-        this.string = (HSSFRichTextString) string;
+        HSSFRichTextString rtr = (HSSFRichTextString)string;
+
+        // If font is not set we must set the default one
+        if (rtr.numFormattingRuns() == 0) rtr.applyFont((short)0);
+
+        this.string = rtr;
     }
 
     /**
diff --git a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/DataExtraction.java b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/DataExtraction.java
new file mode 100755 (executable)
index 0000000..05bf824
--- /dev/null
@@ -0,0 +1,148 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.examples;\r
+\r
+import org.apache.poi.hslf.usermodel.*;\r
+import org.apache.poi.hslf.model.*;\r
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.hwpf.HWPFDocument;\r
+import org.apache.poi.hwpf.usermodel.Range;\r
+import org.apache.poi.hwpf.usermodel.Paragraph;\r
+\r
+import java.io.*;\r
+\r
+/**\r
+ * Demonstrates how you can extract misc embedded data from a ppt file\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class DataExtraction {\r
+\r
+    public static void main(String args[]) throws Exception {\r
+\r
+        if (args.length == 0) {\r
+            usage();\r
+            return;\r
+        }\r
+\r
+        FileInputStream is = new FileInputStream(args[0]);\r
+        SlideShow ppt = new SlideShow(is);\r
+        is.close();\r
+\r
+        //extract all sound files embedded in this presentation\r
+        SoundData[] sound = ppt.getSoundData();\r
+        for (int i = 0; i < sound.length; i++) {\r
+            String type = sound[i].getSoundType();  //*.wav\r
+            String name = sound[i].getSoundName();  //typically file name\r
+            byte[] data = sound[i].getData();       //raw bytes\r
+\r
+            //save the sound  on disk\r
+            FileOutputStream out = new FileOutputStream(name + type);\r
+            out.write(data);\r
+            out.close();\r
+        }\r
+\r
+        //extract embedded OLE documents\r
+        Slide[] slide = ppt.getSlides();\r
+        for (int i = 0; i < slide.length; i++) {\r
+            Shape[] shape = slide[i].getShapes();\r
+            for (int j = 0; j < shape.length; j++) {\r
+                if (shape[j] instanceof OLEShape) {\r
+                    OLEShape ole = (OLEShape) shape[j];\r
+                    ObjectData data = ole.getObjectData();\r
+                    String name = ole.getInstanceName();\r
+                    if ("Worksheet".equals(name)) {\r
+\r
+                        //save xls on disk\r
+                        FileOutputStream out = new FileOutputStream(name + "-("+(j)+").xls");\r
+                        InputStream dis = data.getData();\r
+                        byte[] chunk = new byte[2048];\r
+                        int count;\r
+                        while ((count = dis.read(chunk)) >= 0) {\r
+                          out.write(chunk,0,count);\r
+                        }\r
+                        is.close();\r
+                        out.close();\r
+                    } else if ("Document".equals(name)) {\r
+                        HWPFDocument doc = new HWPFDocument(data.getData());\r
+                        //read the word document\r
+                        Range r = doc.getRange();      \r
+                        for(int k = 0; k < r.numParagraphs(); k++) {\r
+                            Paragraph p = r.getParagraph(k);\r
+                            System.out.println(p.text());\r
+                         }\r
+\r
+                        //save on disk\r
+                        FileOutputStream out = new FileOutputStream(name + "-("+(j)+").doc");\r
+                        doc.write(out);\r
+                        out.close();\r
+                     }  else {\r
+                        System.err.println("Processing " + name);\r
+                    }\r
+                }\r
+\r
+            }\r
+        }\r
+\r
+        //Pictures\r
+        for (int i = 0; i < slide.length; i++) {\r
+            Shape[] shape = slide[i].getShapes();\r
+            for (int j = 0; j < shape.length; j++) {\r
+                if (shape[j] instanceof Picture) {\r
+                    Picture p = (Picture) shape[j];\r
+                    PictureData data = p.getPictureData();\r
+                    String name = p.getPictureName();\r
+                    int type = data.getType();\r
+                    String ext;\r
+                    switch (type) {\r
+                        case Picture.JPEG:\r
+                            ext = ".jpg";\r
+                            break;\r
+                        case Picture.PNG:\r
+                            ext = ".png";\r
+                            break;\r
+                        case Picture.WMF:\r
+                            ext = ".wmf";\r
+                            break;\r
+                        case Picture.EMF:\r
+                            ext = ".emf";\r
+                            break;\r
+                        case Picture.PICT:\r
+                            ext = ".pict";\r
+                            break;\r
+                        case Picture.DIB:\r
+                            ext = ".dib";\r
+                            break;\r
+                        default:\r
+                            continue;\r
+                    }\r
+                    FileOutputStream out = new FileOutputStream("pict-" + j + ext);\r
+                    out.write(data.getData());\r
+                    out.close();\r
+                }\r
+\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    private static void usage(){\r
+        System.out.println("Usage: DataExtraction  ppt");\r
+    }\r
+}\r
diff --git a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/Graphics2DDemo.java b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/Graphics2DDemo.java
new file mode 100755 (executable)
index 0000000..87a59c3
--- /dev/null
@@ -0,0 +1,80 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.examples;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.model.*;\r
+\r
+import java.awt.*;\r
+import java.io.FileOutputStream;\r
+import java.io.FileInputStream;\r
+\r
+/**\r
+ * Demonstrates how to draw into a slide using the HSLF Graphics2D driver.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class Graphics2DDemo {\r
+\r
+    /**\r
+     * A simple bar chart demo\r
+     */\r
+    public static void main(String[] args) throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        //bar chart data. The first value is the bar color, the second is the width\r
+        Object[] def = new Object[]{\r
+            Color.yellow, new Integer(40),\r
+            Color.green, new Integer(60),\r
+            Color.gray, new Integer(30),\r
+            Color.red, new Integer(80),\r
+        };\r
+\r
+        Slide slide = ppt.createSlide();\r
+\r
+        ShapeGroup group = new ShapeGroup();\r
+        //define position of the drawing in the slide\r
+        Rectangle bounds = new java.awt.Rectangle(200, 100, 350, 300);\r
+        group.setAnchor(bounds);\r
+        group.setCoordinates(new java.awt.Rectangle(0, 0, 100, 100));\r
+        slide.addShape(group);\r
+        Graphics2D graphics = new PPGraphics2D(group);\r
+\r
+        //draw a simple bar graph\r
+        int x = 10, y = 10;\r
+        graphics.setFont(new Font("Arial", Font.BOLD, 10));\r
+        for (int i = 0, idx = 1; i < def.length; i+=2, idx++) {\r
+            graphics.setColor(Color.black);\r
+            int width = ((Integer)def[i+1]).intValue();\r
+            graphics.drawString("Q" + idx, x-5, y+10);\r
+            graphics.drawString(width + "%", x + width+3, y + 10);\r
+            graphics.setColor((Color)def[i]);\r
+            graphics.fill(new Rectangle(x, y, width, 10));\r
+            y += 15;\r
+        }\r
+        graphics.setColor(Color.black);\r
+        graphics.setFont(new Font("Arial", Font.BOLD, 14));\r
+        graphics.draw(group.getCoordinates());\r
+        graphics.drawString("Performance", x + 30, y + 10);\r
+\r
+        FileOutputStream out = new FileOutputStream("hslf-graphics.ppt");\r
+        ppt.write(out);\r
+        out.close();\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java
new file mode 100755 (executable)
index 0000000..8a75297
--- /dev/null
@@ -0,0 +1,108 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.examples;\r
+\r
+import org.apache.poi.hslf.usermodel.*;\r
+import org.apache.poi.hslf.model.*;\r
+import org.apache.poi.hslf.record.TextHeaderAtom;\r
+\r
+import javax.imageio.ImageIO;\r
+import java.io.IOException;\r
+import java.io.FileOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.awt.*;\r
+import java.awt.image.BufferedImage;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+/**\r
+ * Demonstrates how you can use HSLF to convert each slide into a PNG image\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class PPT2PNG {\r
+\r
+    public static void main(String args[]) throws Exception {\r
+\r
+        if (args.length == 0) {\r
+            usage();\r
+            return;\r
+        }\r
+\r
+        int slidenum = -1;\r
+        float scale = 1;\r
+        String file = null;\r
+\r
+        for (int i = 0; i < args.length; i++) {\r
+            if (args[i].startsWith("-")) {\r
+                if ("-scale".equals(args[i])){\r
+                    scale = Float.parseFloat(args[++i]);\r
+                } else if ("-slide".equals(args[i])) {\r
+                    slidenum = Integer.parseInt(args[++i]);\r
+                }\r
+            } else {\r
+                file = args[i];\r
+            }\r
+        }\r
+        if(file == null){\r
+            usage();\r
+            return;\r
+        }\r
+\r
+        FileInputStream is = new FileInputStream(file);\r
+        SlideShow ppt = new SlideShow(is);\r
+        is.close();\r
+\r
+        Dimension pgsize = ppt.getPageSize();\r
+        int width = (int)(pgsize.width*scale);\r
+        int height = (int)(pgsize.height*scale);\r
+\r
+        Slide[] slide = ppt.getSlides();\r
+        for (int i = 0; i < slide.length; i++) {\r
+            if (slidenum != -1 && slidenum != (i+1)) continue;\r
+\r
+            String title = slide[i].getTitle();\r
+            System.out.println("Rendering slide "+slide[i].getSlideNumber() + (title == null ? "" : ": " + title));\r
+\r
+            BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\r
+            Graphics2D graphics = img.createGraphics();\r
+            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
+            graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);\r
+            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);\r
+            graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);\r
+\r
+            graphics.setPaint(Color.white);\r
+            graphics.fill(new Rectangle2D.Float(0, 0, width, height));\r
+\r
+            graphics.scale((double)width/pgsize.width, (double)height/pgsize.height);\r
+\r
+            slide[i].draw(graphics);\r
+\r
+            String fname = file.replaceAll("\\.ppt", "-" + (i+1) + ".png");\r
+            FileOutputStream out = new FileOutputStream(fname);\r
+            ImageIO.write(img, "png", out);\r
+            out.close();\r
+        }\r
+    }\r
+\r
+    private static void usage(){\r
+        System.out.println("Usage: PPT2PNG [-scale <scale> -slide <num>] ppt");\r
+    }\r
+}\r
index 29f01b31549c6abef090f8131288af1f96984f76..dc967fd5b4a3c46f6583247720f85421e1e206ef 100644 (file)
@@ -27,22 +27,13 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
 
 import org.apache.poi.POIDocument;
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
 import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
 import org.apache.poi.hslf.exceptions.HSLFException;
-import org.apache.poi.hslf.record.CurrentUserAtom;
-import org.apache.poi.hslf.record.ExOleObjStg;
-import org.apache.poi.hslf.record.PersistPtrHolder;
-import org.apache.poi.hslf.record.PositionDependentRecord;
-import org.apache.poi.hslf.record.Record;
-import org.apache.poi.hslf.record.UserEditAtom;
+import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.usermodel.ObjectData;
 import org.apache.poi.hslf.usermodel.PictureData;
 import org.apache.poi.hslf.model.Shape;
@@ -253,6 +244,7 @@ public class HSLFSlideShow extends POIDocument
 
     private Record[] read(byte[] docstream, int usrOffset){
         ArrayList lst = new ArrayList();
+        HashMap offset2id = new HashMap(); 
         while (usrOffset != 0){
             UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
             lst.add(new Integer(usrOffset));
@@ -266,6 +258,7 @@ public class HSLFSlideShow extends POIDocument
                 Integer offset = (Integer)entries.get(id);
 
                 lst.add(offset);
+                offset2id.put(offset, id);
             }
 
             usrOffset = usr.getLastUserEditAtomOffset();
@@ -278,6 +271,11 @@ public class HSLFSlideShow extends POIDocument
         for (int i = 0; i < a.length; i++) {
             Integer offset = (Integer)a[i];
             rec[i] = (Record)Record.buildRecordAtOffset(docstream, offset.intValue());
+            if(rec[i] instanceof PersistRecord) {
+                PersistRecord psr = (PersistRecord)rec[i];
+                Integer id = (Integer)offset2id.get(offset);
+                psr.setPersistId(id.intValue());
+            }
         }
 
         return rec;
index cf65a9b13cb2cf3007cab12b461a81f71925f08f..299fe55225d239f19373baa44130a2c470787697 100644 (file)
@@ -18,6 +18,9 @@
 package org.apache.poi.hslf.model;
 
 import org.apache.poi.ddf.*;
+import org.apache.poi.util.POILogger;
+
+import java.awt.geom.Rectangle2D;
 
 /**
  * Represents an AutoShape.
@@ -102,4 +105,17 @@ public class AutoShape extends TextShape {
 
         setEscherProperty((short)(EscherProperties.GEOMETRY__ADJUSTVALUE + idx), val);
     }
+
+    public java.awt.Shape getOutline(){
+        ShapeOutline outline = AutoShapes.getShapeOutline(getShapeType());
+        Rectangle2D anchor = getLogicalAnchor2D();
+        if(outline == null){
+            logger.log(POILogger.WARN, "Outline not found for " + ShapeTypes.typeName(getShapeType()));
+            return anchor;
+        } else {
+            java.awt.Shape shape = outline.getOutline(this);
+            return AutoShapes.transform(shape, anchor);
+        }
+    }
+
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java b/src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java
new file mode 100755 (executable)
index 0000000..5d345e6
--- /dev/null
@@ -0,0 +1,373 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.EscherProperties;\r
+\r
+import java.awt.geom.*;\r
+\r
+/**\r
+ * Stores definition of auto-shapes.\r
+ * See the Office Drawing 97-2007 Binary Format Specification for details.\r
+ *\r
+ * TODO: follow the spec and define all the auto-shapes\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class AutoShapes {\r
+    protected static ShapeOutline[] shapes;\r
+\r
+\r
+    /**\r
+     * Return shape outline by shape type\r
+     * @param type shape type see {@link ShapeTypes}\r
+     *\r
+     * @return the shape outline\r
+     */\r
+    public static ShapeOutline getShapeOutline(int type){\r
+        ShapeOutline outline = shapes[type];\r
+        return outline;\r
+    }\r
+\r
+    /**\r
+     * Auto-shapes are defined in the [0,21600] coordinate system.\r
+     * We need to transform it into normal slide coordinates\r
+     *\r
+    */\r
+    public static java.awt.Shape transform(java.awt.Shape outline, Rectangle2D anchor){\r
+        AffineTransform at = new AffineTransform();\r
+        at.translate(anchor.getX(), anchor.getY());\r
+        at.scale(\r
+                1.0f/21600*anchor.getWidth(),\r
+                1.0f/21600*anchor.getHeight()\r
+        );\r
+        return at.createTransformedShape(outline);\r
+    }\r
+\r
+    static {\r
+        shapes = new ShapeOutline[255];\r
+\r
+        shapes[ShapeTypes.Rectangle] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                Rectangle2D path = new Rectangle2D.Float(0, 0, 21600, 21600);\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.RoundRectangle] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+                RoundRectangle2D path = new RoundRectangle2D.Float(0, 0, 21600, 21600, adjval, adjval);\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Ellipse] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                Ellipse2D path = new Ellipse2D.Float(0, 0, 21600, 21600);\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Diamond] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(10800, 0);\r
+                path.lineTo(21600, 10800);\r
+                path.lineTo(10800, 21600);\r
+                path.lineTo(0, 10800);\r
+                path.closePath();\r
+                return path;\r
+           }\r
+        };\r
+\r
+        //m@0,l,21600r21600\r
+        shapes[ShapeTypes.IsocelesTriangle] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 10800);\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(adjval, 0);\r
+                path.lineTo(0, 21600);\r
+                path.lineTo(21600, 21600);\r
+                path.closePath();\r
+                return path;\r
+           }\r
+        };\r
+\r
+        shapes[ShapeTypes.RightTriangle] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(0, 0);\r
+                path.lineTo(21600, 21600);\r
+                path.lineTo(0, 21600);\r
+                path.closePath();\r
+                return path;\r
+           }\r
+        };\r
+\r
+        shapes[ShapeTypes.Parallelogram] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(adjval, 0);\r
+                path.lineTo(21600, 0);\r
+                path.lineTo(21600 - adjval, 21600);\r
+                path.lineTo(0, 21600);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Trapezoid] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(0, 0);\r
+                path.lineTo(adjval, 21600);\r
+                path.lineTo(21600 - adjval, 21600);\r
+                path.lineTo(21600, 0);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Hexagon] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(adjval, 0);\r
+                path.lineTo(21600 - adjval, 0);\r
+                path.lineTo(21600, 10800);\r
+                path.lineTo(21600 - adjval, 21600);\r
+                path.lineTo(adjval, 21600);\r
+                path.lineTo(0, 10800);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Octagon] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 6326);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(adjval, 0);\r
+                path.lineTo(21600 - adjval, 0);\r
+                path.lineTo(21600, adjval);\r
+                path.lineTo(21600, 21600-adjval);\r
+                path.lineTo(21600-adjval, 21600);\r
+                path.lineTo(adjval, 21600);\r
+                path.lineTo(0, 21600-adjval);\r
+                path.lineTo(0, adjval);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Plus] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(adjval, 0);\r
+                path.lineTo(21600 - adjval, 0);\r
+                path.lineTo(21600 - adjval, adjval);\r
+                path.lineTo(21600, adjval);\r
+                path.lineTo(21600, 21600-adjval);\r
+                path.lineTo(21600-adjval, 21600-adjval);\r
+                path.lineTo(21600-adjval, 21600);\r
+                path.lineTo(adjval, 21600);\r
+                path.lineTo(adjval, 21600-adjval);\r
+                path.lineTo(0, 21600-adjval);\r
+                path.lineTo(0, adjval);\r
+                path.lineTo(adjval, adjval);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Pentagon] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(10800, 0);\r
+                path.lineTo(21600, 8259);\r
+                path.lineTo(21600 - 4200, 21600);\r
+                path.lineTo(4200, 21600);\r
+                path.lineTo(0, 8259);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.DownArrow] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                //m0@0 l@1@0 @1,0 @2,0 @2@0,21600@0,10800,21600xe\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 16200);\r
+                int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(0, adjval);\r
+                path.lineTo(adjval2, adjval);\r
+                path.lineTo(adjval2, 0);\r
+                path.lineTo(21600-adjval2, 0);\r
+                path.lineTo(21600-adjval2, adjval);\r
+                path.lineTo(21600, adjval);\r
+                path.lineTo(10800, 21600);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.UpArrow] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                //m0@0 l@1@0 @1,21600@2,21600@2@0,21600@0,10800,xe\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+                int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(0, adjval);\r
+                path.lineTo(adjval2, adjval);\r
+                path.lineTo(adjval2, 21600);\r
+                path.lineTo(21600-adjval2, 21600);\r
+                path.lineTo(21600-adjval2, adjval);\r
+                path.lineTo(21600, adjval);\r
+                path.lineTo(10800, 0);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Arrow] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                //m@0, l@0@1 ,0@1,0@2@0@2@0,21600,21600,10800xe\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 16200);\r
+                int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(adjval, 0);\r
+                path.lineTo(adjval, adjval2);\r
+                path.lineTo(0, adjval2);\r
+                path.lineTo(0, 21600-adjval2);\r
+                path.lineTo(adjval, 21600-adjval2);\r
+                path.lineTo(adjval, 21600);\r
+                path.lineTo(21600, 10800);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.LeftArrow] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                //m@0, l@0@1,21600@1,21600@2@0@2@0,21600,,10800xe\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+                int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(adjval, 0);\r
+                path.lineTo(adjval, adjval2);\r
+                path.lineTo(21600, adjval2);\r
+                path.lineTo(21600, 21600-adjval2);\r
+                path.lineTo(adjval, 21600-adjval2);\r
+                path.lineTo(adjval, 21600);\r
+                path.lineTo(0, 10800);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.Can] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                //m10800,qx0@1l0@2qy10800,21600,21600@2l21600@1qy10800,xem0@1qy10800@0,21600@1nfe\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+\r
+                path.append(new Arc2D.Float(0, 0, 21600, adjval, 0, 180, Arc2D.OPEN), false);\r
+                path.moveTo(0, adjval/2);\r
+\r
+                path.lineTo(0, 21600 - adjval/2);\r
+                path.closePath();\r
+\r
+                path.append(new Arc2D.Float(0, 21600 - adjval, 21600, adjval, 180, 180, Arc2D.OPEN), false);\r
+                path.moveTo(21600, 21600 - adjval/2);\r
+\r
+                path.lineTo(21600, adjval/2);\r
+                path.append(new Arc2D.Float(0, 0, 21600, adjval, 180, 180, Arc2D.OPEN), false);\r
+                path.moveTo(0, adjval/2);\r
+                path.closePath();\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.LeftBrace] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                //m21600,qx10800@0l10800@2qy0@11,10800@3l10800@1qy21600,21600e\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 1800);\r
+                int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 10800);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(21600, 0);\r
+\r
+                path.append(new Arc2D.Float(10800, 0, 21600, adjval*2, 90, 90, Arc2D.OPEN), false);\r
+                path.moveTo(10800, adjval);\r
+\r
+                path.lineTo(10800, adjval2 - adjval);\r
+\r
+                path.append(new Arc2D.Float(-10800, adjval2 - 2*adjval, 21600, adjval*2, 270, 90, Arc2D.OPEN), false);\r
+                path.moveTo(0, adjval2);\r
+\r
+                path.append(new Arc2D.Float(-10800, adjval2, 21600, adjval*2, 0, 90, Arc2D.OPEN), false);\r
+                path.moveTo(10800, adjval2 + adjval);\r
+\r
+                path.lineTo(10800, 21600 - adjval);\r
+\r
+                path.append(new Arc2D.Float(10800, 21600 - 2*adjval, 21600, adjval*2, 180, 90, Arc2D.OPEN), false);\r
+\r
+                return path;\r
+            }\r
+        };\r
+\r
+        shapes[ShapeTypes.RightBrace] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                //m,qx10800@0 l10800@2qy21600@11,10800@3l10800@1qy,21600e\r
+                int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 1800);\r
+                int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 10800);\r
+\r
+                GeneralPath path = new GeneralPath();\r
+                path.moveTo(0, 0);\r
+\r
+                path.append(new Arc2D.Float(-10800, 0, 21600, adjval*2, 0, 90, Arc2D.OPEN), false);\r
+                path.moveTo(10800, adjval);\r
+\r
+                path.lineTo(10800, adjval2 - adjval);\r
+\r
+                path.append(new Arc2D.Float(10800, adjval2 - 2*adjval, 21600, adjval*2, 180, 90, Arc2D.OPEN), false);\r
+                path.moveTo(21600, adjval2);\r
+\r
+                path.append(new Arc2D.Float(10800, adjval2, 21600, adjval*2, 90, 90, Arc2D.OPEN), false);\r
+                path.moveTo(10800, adjval2 + adjval);\r
+\r
+                path.lineTo(10800, 21600 - adjval);\r
+\r
+                path.append(new Arc2D.Float(-10800, 21600 - 2*adjval, 21600, adjval*2, 270, 90, Arc2D.OPEN), false);\r
+\r
+                return path;\r
+            }\r
+        };\r
+\r
+    }\r
+}\r
index 4906922a824f37bcd6200e99e4653c27a4002bda..e2718f6acaa5551bdad9f5bf70de4e36cac01dee 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
 package org.apache.poi.hslf.model;
 
 import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.hslf.usermodel.PictureData;
+import org.apache.poi.hslf.blip.Bitmap;
+import org.apache.poi.util.POILogger;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
 
 /**
  * Background shape
@@ -27,12 +34,42 @@ import org.apache.poi.ddf.EscherContainerRecord;
  */
 public class Background extends Shape {
 
-    protected Background(EscherContainerRecord escherRecord, Shape parent){
+    protected Background(EscherContainerRecord escherRecord, Shape parent) {
         super(escherRecord, parent);
     }
 
-    protected EscherContainerRecord createSpContainer(boolean isChild){
+    protected EscherContainerRecord createSpContainer(boolean isChild) {
         return null;
     }
 
+    public void draw(Graphics2D graphics) {
+        Fill f = getFill();
+        Dimension pg = getSheet().getSlideShow().getPageSize();
+        Rectangle anchor = new Rectangle(0, 0, pg.width, pg.height);
+        switch (f.getFillType()) {
+            case Fill.FILL_SOLID:
+                Color color = f.getForegroundColor();
+                graphics.setPaint(color);
+                graphics.fill(anchor);
+                break;
+            case Fill.FILL_PICTURE:
+                PictureData data = f.getPictureData();
+                if (data instanceof Bitmap) {
+                    BufferedImage img = null;
+                    try {
+                        img = ImageIO.read(new ByteArrayInputStream(data.getData()));
+                    } catch (Exception e) {
+                        logger.log(POILogger.WARN, "ImageIO failed to create image. image.type: " + data.getType());
+                        return;
+                    }
+                    Image scaledImg = img.getScaledInstance(anchor.width, anchor.height, Image.SCALE_SMOOTH);
+                    graphics.drawImage(scaledImg, anchor.x, anchor.y, null);
+
+                }
+                break;
+            default:
+                logger.log(POILogger.WARN, "unsuported fill type: " + f.getFillType());
+                break;
+        }
+    }
 }
index c4df7b6779bd4ebc23806d3ae2989190a8f974f8..49393250d1484232d5385247a67a17d475582c72 100644 (file)
@@ -22,12 +22,10 @@ import org.apache.poi.ddf.*;
 import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.usermodel.PictureData;
 import org.apache.poi.hslf.usermodel.SlideShow;
-import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.POILogFactory;
 
 import java.awt.*;
-import java.util.*;
 
 /**
  * Represents functionality provided by the 'Fill Effects' dialog in PowerPoint.
@@ -137,13 +135,15 @@ public class Fill {
         EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
         EscherSimpleProperty p1 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLCOLOR);
         EscherSimpleProperty p2 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
+        EscherSimpleProperty p3 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLOPACITY);
 
         int p2val = p2 == null ? 0 : p2.getPropertyValue();
+        int alpha =  p3 == null ? 255 : ((p3.getPropertyValue() >> 8) & 0xFF);
 
         Color clr = null;
         if (p1 != null && (p2val  & 0x10) != 0){
             int rgb = p1.getPropertyValue();
-            clr = shape.getColor(rgb);
+            clr = shape.getColor(rgb, alpha);
         }
         return clr;
     }
@@ -176,7 +176,7 @@ public class Fill {
         Color clr = null;
         if (p1 != null && (p2val  & 0x10) != 0){
             int rgb = p1.getPropertyValue();
-            clr = shape.getColor(rgb);
+            clr = shape.getColor(rgb, 255);
         }
         return clr;
     }
index e34d651b3da67bcd37d10271b404a13ac73f506d..d31237f8db1378ce96a436ae8d25ba7d730b1c77 100755 (executable)
@@ -22,6 +22,7 @@ import org.apache.poi.util.POILogger;
 \r
 import java.awt.geom.*;\r
 import java.util.ArrayList;\r
+import java.util.Arrays;\r
 \r
 /**\r
  * A "Freeform" shape.\r
@@ -33,6 +34,16 @@ import java.util.ArrayList;
  * @author Yegor Kozlov\r
  */\r
 public class Freeform extends AutoShape {\r
+\r
+    public static final byte[] SEGMENTINFO_MOVETO   = new byte[]{0x00, 0x40};\r
+    public static final byte[] SEGMENTINFO_LINETO   = new byte[]{0x00, (byte)0xAC};\r
+    public static final byte[] SEGMENTINFO_ESCAPE   = new byte[]{0x01, 0x00};\r
+    public static final byte[] SEGMENTINFO_ESCAPE2  = new byte[]{0x01, 0x20};\r
+    public static final byte[] SEGMENTINFO_CUBICTO  = new byte[]{0x00, (byte)0xAD};\r
+    public static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; //OpenOffice inserts 0xB3 instead of 0xAD.\r
+    public static final byte[] SEGMENTINFO_CLOSE    = new byte[]{0x01, (byte)0x60};\r
+    public static final byte[] SEGMENTINFO_END      = new byte[]{0x00, (byte)0x80};\r
+\r
     /**\r
      * Create a Freeform object and initialize it from the supplied Record container.\r
      *\r
@@ -82,36 +93,37 @@ public class Freeform extends AutoShape {
             switch (type) {\r
                 case PathIterator.SEG_MOVETO:\r
                     pntInfo.add(new Point2D.Double(vals[0], vals[1]));\r
-                    segInfo.add(new byte[]{0x00, 0x40});\r
+                    segInfo.add(SEGMENTINFO_MOVETO);\r
                     break;\r
                 case PathIterator.SEG_LINETO:\r
                     pntInfo.add(new Point2D.Double(vals[0], vals[1]));\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAC});\r
-                    segInfo.add(new byte[]{0x01, 0x00 });\r
+                    segInfo.add(SEGMENTINFO_LINETO);\r
+                    segInfo.add(SEGMENTINFO_ESCAPE);\r
                     break;\r
                 case PathIterator.SEG_CUBICTO:\r
                     pntInfo.add(new Point2D.Double(vals[0], vals[1]));\r
                     pntInfo.add(new Point2D.Double(vals[2], vals[3]));\r
                     pntInfo.add(new Point2D.Double(vals[4], vals[5]));\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAD});\r
-                    segInfo.add(new byte[]{0x01, 0x20 });\r
+                    segInfo.add(SEGMENTINFO_CUBICTO);\r
+                    segInfo.add(SEGMENTINFO_ESCAPE2);\r
                     break;\r
                 case PathIterator.SEG_QUADTO:\r
+                    //TODO: figure out how to convert SEG_QUADTO into SEG_CUBICTO  \r
                     logger.log(POILogger.WARN, "SEG_QUADTO is not supported");\r
                     break;\r
                 case PathIterator.SEG_CLOSE:\r
                     pntInfo.add(pntInfo.get(0));\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAC});\r
-                    segInfo.add(new byte[]{0x01, 0x00 });\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAC});\r
-                    segInfo.add(new byte[]{0x01, (byte)0x60});\r
+                    segInfo.add(SEGMENTINFO_LINETO);\r
+                    segInfo.add(SEGMENTINFO_ESCAPE);\r
+                    segInfo.add(SEGMENTINFO_LINETO);\r
+                    segInfo.add(SEGMENTINFO_CLOSE);\r
                     isClosed = true;\r
                     break;\r
             }\r
 \r
             it.next();\r
         }\r
-        if(!isClosed) segInfo.add(new byte[]{0x00, (byte)0xAC});\r
+        if(!isClosed) segInfo.add(SEGMENTINFO_LINETO);\r
         segInfo.add(new byte[]{0x00, (byte)0x80});\r
 \r
         EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
@@ -147,4 +159,85 @@ public class Freeform extends AutoShape {
 \r
         setAnchor(bounds);\r
     }\r
+\r
+    /**\r
+     * Gets the freeform path\r
+     *\r
+     * @return the freeform path\r
+     */\r
+     public GeneralPath getPath(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4));\r
+\r
+        EscherArrayProperty verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000));\r
+        if(verticesProp == null) verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES));\r
+\r
+        EscherArrayProperty segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000));\r
+        if(segmentsProp == null) segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO));\r
+\r
+        //sanity check\r
+        if(verticesProp == null) {\r
+            logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES ");\r
+            return null;\r
+        }\r
+        if(segmentsProp == null) {\r
+            logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO ");\r
+            return null;\r
+        }\r
+\r
+        Rectangle2D bounds = getAnchor2D();\r
+        float right = (float)bounds.getX();\r
+        float bottom = (float)bounds.getY();\r
+\r
+        GeneralPath path = new GeneralPath();\r
+        int numPoints = verticesProp.getNumberOfElementsInArray();\r
+        int numSegments = segmentsProp.getNumberOfElementsInArray();\r
+        for (int i = 0, j = 0; i < numSegments && j < numPoints; i++) {\r
+            byte[] elem = segmentsProp.getElement(i);\r
+            if(Arrays.equals(elem, SEGMENTINFO_MOVETO)){\r
+                byte[] p = verticesProp.getElement(j++);\r
+                short x = LittleEndian.getShort(p, 0);\r
+                short y = LittleEndian.getShort(p, 2);\r
+                path.moveTo(\r
+                        ((float)x*POINT_DPI/MASTER_DPI + right),\r
+                        ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+            } else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){\r
+                i++;\r
+                byte[] p1 = verticesProp.getElement(j++);\r
+                short x1 = LittleEndian.getShort(p1, 0);\r
+                short y1 = LittleEndian.getShort(p1, 2);\r
+                byte[] p2 = verticesProp.getElement(j++);\r
+                short x2 = LittleEndian.getShort(p2, 0);\r
+                short y2 = LittleEndian.getShort(p2, 2);\r
+                byte[] p3 = verticesProp.getElement(j++);\r
+                short x3 = LittleEndian.getShort(p3, 0);\r
+                short y3 = LittleEndian.getShort(p3, 2);\r
+                path.curveTo(\r
+                        ((float)x1*POINT_DPI/MASTER_DPI + right), ((float)y1*POINT_DPI/MASTER_DPI + bottom),\r
+                        ((float)x2*POINT_DPI/MASTER_DPI + right), ((float)y2*POINT_DPI/MASTER_DPI + bottom),\r
+                        ((float)x3*POINT_DPI/MASTER_DPI + right), ((float)y3*POINT_DPI/MASTER_DPI + bottom));\r
+\r
+            } else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){\r
+                i++;\r
+                byte[] pnext = segmentsProp.getElement(i);\r
+                if(Arrays.equals(pnext, SEGMENTINFO_ESCAPE)){\r
+                    if(j + 1 < numPoints){\r
+                        byte[] p = verticesProp.getElement(j++);\r
+                        short x = LittleEndian.getShort(p, 0);\r
+                        short y = LittleEndian.getShort(p, 2);\r
+                        path.lineTo(\r
+                                ((float)x*POINT_DPI/MASTER_DPI + right), ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+                    }\r
+                } else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){\r
+                    path.closePath();\r
+                }\r
+            }\r
+        }\r
+\r
+        return path;\r
+    }\r
+\r
+    public java.awt.Shape getOutline(){\r
+        return getPath();\r
+    }\r
 }\r
index 9237183689aef981e9985767d16b3a0a08949d49..370da96d6b17b7da763b080ff232f308973bcf07 100644 (file)
@@ -19,6 +19,9 @@ package org.apache.poi.hslf.model;
 
 import org.apache.poi.ddf.*;
 
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.Line2D;
+
 /**
  * Represents a line in a PowerPoint drawing
  *
@@ -126,4 +129,8 @@ public class Line extends SimpleShape {
         return _escherContainer;
     }
 
+    public java.awt.Shape getOutline(){
+        Rectangle2D anchor = getLogicalAnchor2D();
+        return new Line2D.Double(anchor.getX(), anchor.getY(), anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight());
+    }
 }
index 4371e2a371ffdf4481c5509efdec0f726efb7c4e..5b1b1016e4f3487875d03a2a6ac8665003cd0db2 100644 (file)
@@ -17,6 +17,8 @@
 package org.apache.poi.hslf.model;
 
 import org.apache.poi.hslf.record.SheetContainer;
+import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.record.RecordTypes;
 import org.apache.poi.hslf.model.textproperties.TextProp;
 
 /**
@@ -37,4 +39,32 @@ public abstract class MasterSheet extends Sheet {
      */
     public abstract TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) ;
 
+
+    /**
+     * Checks if the shape is a placeholder.
+     * (placeholders aren't normal shapes, they are visible only in the Edit Master mode)
+     *
+     *
+     * @return true if the shape is a placeholder
+     */
+    public static boolean isPlaceholder(Shape shape){
+        if(!(shape instanceof TextShape)) return false;
+
+        TextShape tx = (TextShape)shape;
+        TextRun run = tx.getTextRun();
+        if(run == null) return false;
+
+        Record[] records = run._records;
+        for (int i = 0; i < records.length; i++) {
+            int type = (int)records[i].getRecordType();
+            if (type == RecordTypes.BaseTextPropAtom.typeID ||
+                type == RecordTypes.DateTimeMCAtom.typeID ||
+                type == RecordTypes.GenericDateMCAtom.typeID ||
+                type == RecordTypes.FooterMCAtom.typeID ||
+                type == RecordTypes.SlideNumberMCAtom.typeID
+                    ) return true;
+
+        }
+        return false;
+    }
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
new file mode 100755 (executable)
index 0000000..3f574c3
--- /dev/null
@@ -0,0 +1,160 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  You may obtain a copy of the License at\r
+*\r
+*     http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.usermodel.ObjectData;\r
+import org.apache.poi.hslf.record.ExObjList;\r
+import org.apache.poi.hslf.record.Record;\r
+import org.apache.poi.hslf.record.ExEmbed;\r
+import org.apache.poi.util.POILogger;\r
+\r
+\r
+/**\r
+ * A shape representing embedded OLE obejct.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class OLEShape extends Picture {\r
+    protected ExEmbed _exEmbed;\r
+\r
+    /**\r
+     * Create a new <code>OLEShape</code>\r
+     *\r
+    * @param idx the index of the picture\r
+     */\r
+    public OLEShape(int idx){\r
+        super(idx);\r
+    }\r
+\r
+    /**\r
+     * Create a new <code>OLEShape</code>\r
+     *\r
+     * @param idx the index of the picture\r
+     * @param parent the parent shape\r
+     */\r
+    public OLEShape(int idx, Shape parent) {\r
+        super(idx, parent);\r
+    }\r
+\r
+    /**\r
+      * Create a <code>OLEShape</code> object\r
+      *\r
+      * @param escherRecord the <code>EscherSpContainer</code> record which holds information about\r
+      *        this picture in the <code>Slide</code>\r
+      * @param parent the parent shape of this picture\r
+      */\r
+     protected OLEShape(EscherContainerRecord escherRecord, Shape parent){\r
+        super(escherRecord, parent);\r
+    }\r
+\r
+    /**\r
+     * Returns unique identifier for the OLE object.\r
+     *\r
+     * @return the unique identifier for the OLE object\r
+     */\r
+    public int getObjectID(){\r
+        return getEscherProperty(EscherProperties.BLIP__PICTUREID);\r
+    }\r
+\r
+    /**\r
+     * Returns unique identifier for the OLE object.\r
+     *\r
+     * @return the unique identifier for the OLE object\r
+     */\r
+    public ObjectData getObjectData(){\r
+        SlideShow ppt = getSheet().getSlideShow();\r
+        ObjectData[] ole = ppt.getEmbeddedObjects();\r
+\r
+        //persist reference\r
+        int ref = getExEmbed().getExOleObjAtom().getObjStgDataRef();\r
+        for (int i = 0; i < ole.length; i++) {\r
+            if(ole[i].getExOleObjStg().getPersistId() == ref) return ole[i];\r
+\r
+        }\r
+        logger.log(POILogger.WARN, "OLE data not found");\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * Return the record container for this embedded object.\r
+     *\r
+     * <p>\r
+     * It contains:\r
+     * 1. ExEmbedAtom.(4045)\r
+     * 2. ExOleObjAtom (4035)\r
+     * 3. CString (4026), Instance MenuName (1) used for menus and the Links dialog box.\r
+     * 4. CString (4026), Instance ProgID (2) that stores the OLE Programmatic Identifier.\r
+     *     A ProgID is a string that uniquely identifies a given object.\r
+     * 5. CString (4026), Instance ClipboardName (3) that appears in the paste special dialog.\r
+     * 6. MetaFile( 4033), optional\r
+     * </p>\r
+     * @return\r
+     */\r
+    public ExEmbed getExEmbed(){\r
+        if(_exEmbed == null){\r
+            SlideShow ppt = getSheet().getSlideShow();\r
+\r
+            ExObjList lst = ppt.getDocumentRecord().getExObjList();\r
+            if(lst == null){\r
+                logger.log(POILogger.WARN, "ExObjList not found");\r
+                return null;\r
+            }\r
+\r
+            int id = getObjectID();\r
+            Record[] ch = lst.getChildRecords();\r
+            for (int i = 0; i < ch.length; i++) {\r
+                if(ch[i] instanceof ExEmbed){\r
+                    ExEmbed embd = (ExEmbed)ch[i];\r
+                    if( embd.getExOleObjAtom().getObjID() == id) _exEmbed = embd;\r
+                }\r
+            }\r
+        }\r
+        return _exEmbed;\r
+    }\r
+\r
+    /**\r
+     * Returns the instance name of the embedded object, e.g. "Document" or "Workbook".\r
+     *\r
+     * @return the instance name of the embedded object\r
+     */\r
+    public String getInstanceName(){\r
+        return getExEmbed().getMenuName();\r
+    }\r
+\r
+    /**\r
+     * Returns the full name of the embedded object,\r
+     *  e.g. "Microsoft Word Document" or "Microsoft Office Excel Worksheet".\r
+     *\r
+     * @return the full name of the embedded object\r
+     */\r
+    public String getFullName(){\r
+        return getExEmbed().getClipboardName();\r
+    }\r
+\r
+    /**\r
+     * Returns the ProgID that stores the OLE Programmatic Identifier.\r
+     * A ProgID is a string that uniquely identifies a given object, for example,\r
+     * "Word.Document.8" or "Excel.Sheet.8".\r
+     *\r
+     * @return the ProgID\r
+     */\r
+    public String getProgID(){\r
+        return getExEmbed().getProgId();\r
+    }\r
+}\r
index 23088f056b5ff34dc1403e4e87134f4e7f392bb1..37cedce0fabcb36dfebeb3a8e73cfd859dbbd5a1 100644 (file)
@@ -27,7 +27,6 @@ import java.awt.image.renderable.RenderableImage;
 import java.awt.geom.*;
 import java.text.AttributedCharacterIterator;
 import java.util.Map;
-import java.util.ArrayList;
 import org.apache.poi.hslf.usermodel.RichTextRun;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.util.POILogger;
index 910d5c850344e79d12dc4aa68cd65e5bcaeb68b7..e10986966bf85532c169a7aa8a87da1350175ec2 100644 (file)
@@ -21,13 +21,16 @@ import org.apache.poi.hslf.usermodel.PictureData;
 import org.apache.poi.hslf.usermodel.SlideShow;
 import org.apache.poi.hslf.record.Document;
 import org.apache.poi.hslf.blip.Bitmap;
+import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.util.POILogger;
 
 import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.awt.*;
+import java.awt.geom.Rectangle2D;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.util.List;
 import java.util.Arrays;
 
@@ -128,7 +131,7 @@ public class Picture extends SimpleShape {
 
         //set default properties for a picture
         EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 8388736);
+        setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x800080);
 
         //another weird feature of powerpoint: for picture id we must add 0x4000.
         setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx);
@@ -192,14 +195,70 @@ public class Picture extends SimpleShape {
         return null;
     }
 
+    /**
+     * Name of this picture.
+     *
+     * @return name of this picture
+     */
+    public String getPictureName(){
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
+        EscherComplexProperty prop = (EscherComplexProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPFILENAME);
+        String name = null;
+        if(prop != null){
+            try {
+                name = new String(prop.getComplexData(), "UTF-16LE");
+                int idx = name.indexOf('\u0000');
+                return idx == -1 ? name : name.substring(0, idx);
+            } catch (UnsupportedEncodingException e){
+                throw new HSLFException(e);
+            }
+        }
+        return name;
+    }
+
+    /**
+     * Name of this picture.
+     *
+     * @param name of this picture
+     */
+    public void setPictureName(String name){
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
+        try {
+            byte[] data = (name + '\u0000').getBytes("UTF-16LE");
+            EscherComplexProperty prop = new EscherComplexProperty(EscherProperties.BLIP__BLIPFILENAME, false, data);
+            opt.addEscherProperty(prop);
+        } catch (UnsupportedEncodingException e){
+            throw new HSLFException(e);
+        }
+    }
+
     /**
      * By default set the orininal image size
      */
     protected void afterInsert(Sheet sh){
+        super.afterInsert(sh);
         java.awt.Rectangle anchor = getAnchor();
         if (anchor.equals(new java.awt.Rectangle())){
             setDefaultSize();
         }
     }
 
+    public void draw(Graphics2D graphics){
+        PictureData data = getPictureData();
+        if (data  instanceof Bitmap){
+            BufferedImage img = null;
+            try {
+                       img = ImageIO.read(new ByteArrayInputStream(data.getData()));
+            }
+            catch (Exception e){
+                logger.log(POILogger.WARN, "ImageIO failed to create image. image.type: " + data.getType());
+                return;
+            }
+            Rectangle anchor = getAnchor();
+            Image scaledImg = img.getScaledInstance(anchor.width, anchor.height, Image.SCALE_SMOOTH);
+            graphics.drawImage(scaledImg, anchor.x, anchor.y, null);
+        } else {
+            logger.log(POILogger.WARN, "Rendering of metafiles is not yet supported. image.type: " + (data == null ? "NA" : data.getClass().getName()));
+        }
+    }
 }
index f93392a9a9bb601bc51eece34294e63b21c5e62e..e96e349002434786e473a7e9b3f79cfdc5e6d24d 100644 (file)
 package org.apache.poi.hslf.model;
 
 import org.apache.poi.ddf.*;
-import org.apache.poi.hslf.model.ShapeTypes;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
+import org.apache.poi.hslf.record.PPDrawing;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.POILogFactory;
 
-import java.util.Iterator;
+import java.util.*;
 import java.awt.*;
 import java.awt.geom.Rectangle2D;
 
@@ -166,12 +166,24 @@ public abstract class Shape {
         if ((flags & EscherSpRecord.FLAG_CHILD) != 0){
             EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID);
             anchor = new java.awt.Rectangle();
-            anchor = new Rectangle2D.Float(
-                (float)rec.getDx1()*POINT_DPI/MASTER_DPI,
-                (float)rec.getDy1()*POINT_DPI/MASTER_DPI,
-                (float)(rec.getDx2()-rec.getDx1())*POINT_DPI/MASTER_DPI,
-                (float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI
-            );
+            if(rec == null){
+                logger.log(POILogger.WARN, "EscherSpRecord.FLAG_CHILD is set but EscherChildAnchorRecord was not found");
+                EscherClientAnchorRecord clrec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID);
+                anchor = new java.awt.Rectangle();
+                anchor = new Rectangle2D.Float(
+                    (float)clrec.getCol1()*POINT_DPI/MASTER_DPI,
+                    (float)clrec.getFlag()*POINT_DPI/MASTER_DPI,
+                    (float)(clrec.getDx1()-clrec.getCol1())*POINT_DPI/MASTER_DPI,
+                    (float)(clrec.getRow1()-clrec.getFlag())*POINT_DPI/MASTER_DPI
+                );
+            } else {
+                anchor = new Rectangle2D.Float(
+                    (float)rec.getDx1()*POINT_DPI/MASTER_DPI,
+                    (float)rec.getDy1()*POINT_DPI/MASTER_DPI,
+                    (float)(rec.getDx2()-rec.getDx1())*POINT_DPI/MASTER_DPI,
+                    (float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI
+                );
+            }
         }
         else {
             EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID);
@@ -186,6 +198,10 @@ public abstract class Shape {
         return anchor;
     }
 
+    public Rectangle2D getLogicalAnchor2D(){
+        return getAnchor2D();
+    }
+
     /**
      * Sets the anchor (the bounding box rectangle) of this shape.
      * All coordinates should be expressed in points (72 dpi).
@@ -245,7 +261,7 @@ public abstract class Shape {
      * @return escher property or <code>null</code> if not found.
      */
      public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){
-        for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); )
+        if(opt != null) for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); )
         {
             EscherProperty prop = (EscherProperty) iterator.next();
             if (prop.getPropertyNumber() == propId)
@@ -294,7 +310,18 @@ public abstract class Shape {
    public int getEscherProperty(short propId){
         EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
         EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId);
-        return prop == null ? 0 : prop.getPropertyNumber();
+        return prop == null ? 0 : prop.getPropertyValue();
+    }
+
+    /**
+     * Get the value of a simple escher property for this shape.
+     *
+     * @param propId    The id of the property. One of the constants defined in EscherOptRecord.
+     */
+   public int getEscherProperty(short propId, int defaultValue){
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId);
+        return prop == null ? defaultValue : prop.getPropertyValue();
     }
 
     /**
@@ -314,7 +341,58 @@ public abstract class Shape {
      * @param sh - owning shape
      */
     protected void afterInsert(Sheet sh){
+        PPDrawing ppdrawing = sh.getPPDrawing();
+
+        EscherContainerRecord dgContainer = (EscherContainerRecord) ppdrawing.getEscherRecords()[0];
+
+        EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
+
+        int id = allocateShapeId(dg);
+        setShapeId(id);
+    }
+
+    /**
+     * Allocates new shape id for the new drawing group id.
+     *
+     * @param dg  EscherDgRecord of the sheet that owns the shape being created
+     *
+     * @return a new shape id.
+     */
+    protected int allocateShapeId(EscherDgRecord dg)
+    {
+        EscherDggRecord dgg = _sheet.getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
+        if(dgg == null){
+            logger.log(POILogger.ERROR, "EscherDggRecord not found");
+            return 0;
+        }
+
+        dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
+
+        // Add to existing cluster if space available
+        for (int i = 0; i < dgg.getFileIdClusters().length; i++)
+        {
+            EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
+            if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024)
+            {
+                int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
+                c.incrementShapeId();
+                dg.setNumShapes( dg.getNumShapes() + 1 );
+                dg.setLastMSOSPID( result );
+                if (result >= dgg.getShapeIdMax())
+                    dgg.setShapeIdMax( result + 1 );
+                return result;
+            }
+        }
 
+        // Create new cluster
+        dgg.addCluster( dg.getDrawingGroupId(), 0 );
+        dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
+        dg.setNumShapes( dg.getNumShapes() + 1 );
+        int result = (1024 * dgg.getFileIdClusters().length);
+        dg.setLastMSOSPID( result );
+        if (result >= dgg.getShapeIdMax())
+            dgg.setShapeIdMax( result + 1 );
+        return result;
     }
 
     /**
@@ -333,14 +411,32 @@ public abstract class Shape {
         _sheet = sheet;
     }
 
-    protected Color getColor(int rgb){
+    protected Color getColor(int rgb, int alpha){
         if (rgb >= 0x8000000) {
             int idx = rgb - 0x8000000;
             ColorSchemeAtom ca = getSheet().getColorScheme();
             if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx);
         }
         Color tmp = new Color(rgb, true);
-        return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
+        return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed(), alpha);
+    }
+
+    /**
+     * @return id for the shape.
+     */
+    public int getShapeId(){
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
+        return spRecord == null ? 0 : spRecord.getShapeId();
+    }
+
+    /**
+     * Sets shape ID
+     *
+     * @param id of the shape
+     */
+    public void setShapeId(int id){
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
+        if(spRecord != null) spRecord.setShapeId(id);
     }
 
     /**
@@ -364,4 +460,16 @@ public abstract class Shape {
         return Hyperlink.find(this);
     }
 
+    public void draw(Graphics2D graphics){
+        logger.log(POILogger.INFO, "Rendering " + getShapeName());
+    }
+
+    /**
+     * Return shape outline as a java.awt.Shape object
+     *
+     * @return the shape outline
+     */
+    public java.awt.Shape getOutline(){
+        return getLogicalAnchor2D();
+    }
 }
index 76b43ebb8acae89773df3990942e1faab194ce89..4abc8c1c0040358285b47c1f743aed0fe20419ad 100644 (file)
@@ -76,13 +76,19 @@ public class ShapeFactory {
             case ShapeTypes.TextBox:
                 shape = new TextBox(spContainer, parent);
                 break;
-            case ShapeTypes.PictureFrame:
-                shape = new Picture(spContainer, parent);
+            case ShapeTypes.PictureFrame: {
+                EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
+                EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.BLIP__PICTUREID);
+                if(prop != null)
+                    shape = new OLEShape(spContainer, parent); //presence of BLIP__PICTUREID indicates it is an embedded object 
+                else
+                    shape = new Picture(spContainer, parent);
                 break;
+            }
             case ShapeTypes.Line:
                 shape = new Line(spContainer, parent);
                 break;
-            case ShapeTypes.NotPrimitive:
+            case ShapeTypes.NotPrimitive: {
                 EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
                 EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES);
                 if(prop != null)
@@ -93,6 +99,7 @@ public class ShapeFactory {
                     shape = new AutoShape(spContainer, parent);
                 }
                 break;
+            }
             default:
                 shape = new AutoShape(spContainer, parent);
                 break;
index b22ed8d70b1222f0041b2e6a257cb6439383780a..b57a6e2336d6ba75527d622386877040aa238007 100644 (file)
@@ -24,6 +24,8 @@ import org.apache.poi.hslf.record.EscherTextboxWrapper;
 import java.util.ArrayList;
 import java.util.List;
 import java.awt.geom.Rectangle2D;
+import java.awt.geom.AffineTransform;
+import java.awt.*;
 
 /**
  *  Represents a group of shapes.
@@ -113,6 +115,47 @@ public class ShapeGroup extends Shape{
         spgr.setRectY2((anchor.y + anchor.height)*MASTER_DPI/POINT_DPI);
     }
 
+    /**
+     * Sets the coordinate space of this group.  All children are constrained
+     * to these coordinates.
+     *
+     * @param anchor the coordinate space of this group
+     */
+    public void setCoordinates(Rectangle2D anchor){
+        EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0);
+        EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID);
+
+        int x1 = (int)Math.round(anchor.getX()*MASTER_DPI/POINT_DPI);
+        int y1 = (int)Math.round(anchor.getY()*MASTER_DPI/POINT_DPI);
+        int x2 = (int)Math.round((anchor.getX() + anchor.getWidth())*MASTER_DPI/POINT_DPI);
+        int y2 = (int)Math.round((anchor.getY() + anchor.getHeight())*MASTER_DPI/POINT_DPI);
+
+        spgr.setRectX1(x1);
+        spgr.setRectY1(y1);
+        spgr.setRectX2(x2);
+        spgr.setRectY2(y2);
+
+    }
+
+    /**
+     * Gets the coordinate space of this group.  All children are constrained
+     * to these coordinates.
+     *
+     * @return the coordinate space of this group
+     */
+    public Rectangle2D getCoordinates(){
+        EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0);
+        EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID);
+
+        Rectangle2D.Float anchor = new Rectangle2D.Float();
+        anchor.x = (float)spgr.getRectX1()*POINT_DPI/MASTER_DPI;
+        anchor.y = (float)spgr.getRectY1()*POINT_DPI/MASTER_DPI;
+        anchor.width = (float)(spgr.getRectX2() - spgr.getRectX1())*POINT_DPI/MASTER_DPI;
+        anchor.height = (float)(spgr.getRectY2() - spgr.getRectY1())*POINT_DPI/MASTER_DPI;
+
+        return anchor;
+    }
+
     /**
      * Create a new ShapeGroup and create an instance of <code>EscherSpgrContainer</code> which represents a group of shapes
      */
@@ -190,14 +233,24 @@ public class ShapeGroup extends Shape{
      * @return the anchor of this shape group
      */
     public Rectangle2D getAnchor2D(){
-        EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0);
-        EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(groupInfoContainer, EscherSpgrRecord.RECORD_ID);
-        Rectangle2D anchor = new Rectangle2D.Float(
-            (float)spgr.getRectX1()*POINT_DPI/MASTER_DPI,
-            (float)spgr.getRectY1()*POINT_DPI/MASTER_DPI,
-            (float)(spgr.getRectX2() - spgr.getRectX1())*POINT_DPI/MASTER_DPI,
-            (float)(spgr.getRectY2() - spgr.getRectY1())*POINT_DPI/MASTER_DPI
-        );
+        EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0);
+        EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID);
+        Rectangle2D.Float anchor = new Rectangle2D.Float();
+        if(clientAnchor == null){
+            logger.log(POILogger.INFO, "EscherClientAnchorRecord was not found for shape group. Searching for EscherChildAnchorRecord.");
+            EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(spContainer, EscherChildAnchorRecord.RECORD_ID);
+            anchor = new Rectangle2D.Float(
+                (float)rec.getDx1()*POINT_DPI/MASTER_DPI,
+                (float)rec.getDy1()*POINT_DPI/MASTER_DPI,
+                (float)(rec.getDx2()-rec.getDx1())*POINT_DPI/MASTER_DPI,
+                (float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI
+            );
+        } else {
+            anchor.x = (float)clientAnchor.getCol1()*POINT_DPI/MASTER_DPI;
+            anchor.y = (float)clientAnchor.getFlag()*POINT_DPI/MASTER_DPI;
+            anchor.width = (float)(clientAnchor.getDx1() - clientAnchor.getCol1())*POINT_DPI/MASTER_DPI ;
+            anchor.height = (float)(clientAnchor.getRow1() - clientAnchor.getFlag())*POINT_DPI/MASTER_DPI;
+        }
 
         return anchor;
     }
@@ -222,5 +275,27 @@ public class ShapeGroup extends Shape{
      public Hyperlink getHyperlink(){
         return null;
     }
-    
+
+    public void draw(Graphics2D graphics){
+        Rectangle2D anchor = getAnchor2D();
+        Rectangle2D coords = getCoordinates();
+
+        //transform coordinates
+        AffineTransform at = graphics.getTransform();
+        /*
+        if(!anchor.equals(coords)){
+            graphics.scale(anchor.getWidth()/coords.getWidth(), anchor.getHeight()/coords.getHeight());
+
+            graphics.translate(
+                    anchor.getX()*coords.getWidth()/anchor.getWidth() - coords.getX(),
+                    anchor.getY()*coords.getHeight()/anchor.getHeight() - coords.getY());
+        }
+        */
+        Shape[] sh = getShapes();
+        for (int i = 0; i < sh.length; i++) {
+            sh[i].draw(graphics);
+        }
+
+        graphics.setTransform(at);
+    }
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeOutline.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeOutline.java
new file mode 100755 (executable)
index 0000000..069fdbc
--- /dev/null
@@ -0,0 +1,27 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+/**\r
+ * Date: Apr 17, 2008\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public interface ShapeOutline {\r
+    java.awt.Shape getOutline(Shape shape);\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapePainter.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapePainter.java
new file mode 100755 (executable)
index 0000000..fa947dd
--- /dev/null
@@ -0,0 +1,102 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+\r
+import org.apache.poi.util.POILogger;\r
+import org.apache.poi.util.POILogFactory;\r
+\r
+import java.awt.*;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+/**\r
+ * Paint a shape into java.awt.Graphics2D\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class ShapePainter {\r
+    protected static POILogger logger = POILogFactory.getLogger(ShapePainter.class);\r
+\r
+    public static void paint(SimpleShape shape, Graphics2D graphics){\r
+        Rectangle2D anchor = shape.getLogicalAnchor2D();\r
+        java.awt.Shape outline = shape.getOutline();\r
+\r
+        //flip vertical\r
+        if(shape.getFlipVertical()){\r
+            graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());\r
+            graphics.scale(1, -1);\r
+            graphics.translate(-anchor.getX(), -anchor.getY());\r
+        }\r
+        //flip horizontal\r
+        if(shape.getFlipHorizontal()){\r
+            graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());\r
+            graphics.scale(-1, 1);\r
+            graphics.translate(-anchor.getX() , -anchor.getY());\r
+        }\r
+\r
+        //rotate transform\r
+        double angle = shape.getRotation();\r
+\r
+        if(angle != 0){\r
+            double centerX = anchor.getX() + anchor.getWidth()/2;\r
+            double centerY = anchor.getY() + anchor.getHeight()/2;\r
+\r
+            graphics.translate(centerX, centerY);\r
+            graphics.rotate(Math.toRadians(angle));\r
+            graphics.translate(-centerX, -centerY);\r
+        }\r
+\r
+        //fill\r
+        Color fillColor = shape.getFill().getForegroundColor();\r
+        if (fillColor != null) {\r
+            //TODO: implement gradient and texture fill patterns\r
+            graphics.setPaint(fillColor);\r
+            graphics.fill(outline);\r
+        }\r
+\r
+        //border\r
+        Color lineColor = shape.getLineColor();\r
+        if (lineColor != null){\r
+            graphics.setPaint(lineColor);\r
+            float width = (float)shape.getLineWidth();\r
+            if(width == 0) width = 0.75f;\r
+\r
+            int dashing = shape.getLineDashing();\r
+            //TODO: implement more dashing styles\r
+            float[] dashptrn = null;\r
+            switch(dashing){\r
+                case Line.PEN_SOLID:\r
+                    dashptrn = null;\r
+                    break;\r
+                case Line.PEN_PS_DASH:\r
+                    dashptrn = new float[]{width, width};\r
+                    break;\r
+                case Line.PEN_DOTGEL:\r
+                    dashptrn = new float[]{width*4, width*3};\r
+                    break;\r
+               default:\r
+                    logger.log(POILogger.WARN, "unsupported dashing: " + dashing);\r
+                    dashptrn = new float[]{width, width};\r
+                    break;\r
+            }\r
+\r
+            Stroke stroke = new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dashptrn, 0.0f);\r
+            graphics.setStroke(stroke);\r
+            graphics.draw(outline);\r
+        }\r
+    }\r
+}\r
index abf2fec5890050779205c21f3edfc7627a46b51f..d9c8903d5bb3038ea927defe49aa78bb5218f78d 100644 (file)
@@ -29,6 +29,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
+import java.awt.*;
 
 /**
  * This class defines the common format of "Sheets" in a powerpoint
@@ -246,14 +247,6 @@ public abstract class Sheet {
         EscherContainerRecord spgr = (EscherContainerRecord) Shape.getEscherChild(dgContainer, EscherContainerRecord.SPGR_CONTAINER);
         spgr.addChildRecord(shape.getSpContainer());
 
-        EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
-        dg.setNumShapes(dg.getNumShapes() + 1);
-
-        int shapeId = dg.getLastMSOSPID()+1;
-        dg.setLastMSOSPID(shapeId);
-
-        EscherSpRecord sp = shape.getSpContainer().getChildById(EscherSpRecord.RECORD_ID);
-        if(sp != null) sp.setShapeId(shapeId);
         shape.setSheet(this);
         shape.afterInsert(this);
 
@@ -329,4 +322,7 @@ public abstract class Sheet {
         return _background;
     }
 
+    public void draw(Graphics2D graphics){
+
+    }
 }
index 0831d453b5925b35de5f838cc6b606203051bb4f..ea0719a7f15a7e5635c0064584a058b16c4fad39 100644 (file)
@@ -22,6 +22,8 @@ import org.apache.poi.util.LittleEndian;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
 
 import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
 
 /**
  *  An abstract simple (non-group) shape.
@@ -199,4 +201,87 @@ public class SimpleShape extends Shape {
         getFill().setForegroundColor(color);
     }
 
+    /**
+     * Whether the shape is horizontally flipped
+     *
+     * @return whether the shape is horizontally flipped
+     */
+     public boolean getFlipHorizontal(){
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
+        return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPHORIZ) != 0;
+    }
+
+    /**
+     * Whether the shape is vertically flipped
+     *
+     * @return whether the shape is vertically flipped
+     */
+    public boolean getFlipVertical(){
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
+        return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPVERT) != 0;
+    }
+
+    /**
+     * Rotation angle in degrees
+     *
+     * @return rotation angle in degrees
+     */
+    public int getRotation(){
+        int rot = getEscherProperty(EscherProperties.TRANSFORM__ROTATION);
+        int angle = (rot >> 16) % 360;
+
+        return angle;
+    }
+
+    public Rectangle2D getLogicalAnchor2D(){
+        Rectangle2D anchor = getAnchor2D();
+
+        //if it is a groupped shape see if we need to transform the coordinates
+        if (_parent != null){
+            Shape top = _parent;
+            while(top.getParent() != null) top = top.getParent();
+
+            Rectangle2D clientAnchor = top.getAnchor2D();
+            Rectangle2D spgrAnchor = ((ShapeGroup)top).getCoordinates();
+
+            double scalex = (double)spgrAnchor.getWidth()/clientAnchor.getWidth();
+            double scaley = (double)spgrAnchor.getHeight()/clientAnchor.getHeight();
+
+            double x = clientAnchor.getX() + (anchor.getX() - spgrAnchor.getX())/scalex;
+            double y = clientAnchor.getY() + (anchor.getY() - spgrAnchor.getY())/scaley;
+            double width = anchor.getWidth()/scalex;
+            double height = anchor.getHeight()/scaley;
+
+            anchor = new Rectangle2D.Double(x, y, width, height);
+
+        }
+
+        int angle = getRotation();
+        if(angle != 0){
+            double centerX = anchor.getX() + anchor.getWidth()/2;
+            double centerY = anchor.getY() + anchor.getHeight()/2;
+
+            AffineTransform trans = new AffineTransform();
+            trans.translate(centerX, centerY);
+            trans.rotate(Math.toRadians(angle));
+            trans.translate(-centerX, -centerY);
+
+            Rectangle2D rect = trans.createTransformedShape(anchor).getBounds2D();
+            if((anchor.getWidth() < anchor.getHeight() && rect.getWidth() > rect.getHeight()) ||
+                (anchor.getWidth() > anchor.getHeight() && rect.getWidth() < rect.getHeight())    ){
+                trans = new AffineTransform();
+                trans.translate(centerX, centerY);
+                trans.rotate(Math.PI/2);
+                trans.translate(-centerX, -centerY);
+                anchor = trans.createTransformedShape(anchor).getBounds2D();
+            }
+        }
+        return anchor;
+    }
+
+    public void draw(Graphics2D graphics){
+        AffineTransform at = graphics.getTransform();
+        ShapePainter.paint(this, graphics);
+        graphics.setTransform(at);
+    }
 }
index ea7201751d79310cb73d43925f549a8779c06e02..e618ae23033372b2869a996a0a9a84906a984396 100644 (file)
 package org.apache.poi.hslf.model;
 
 import java.util.Vector;
-import java.util.List;
-import java.util.Iterator;
-import java.util.ArrayList;
+import java.awt.*;
 
-import org.apache.poi.hslf.record.PPDrawing;
 import org.apache.poi.hslf.record.SlideAtom;
 import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherRecord;
 
 /**
  * This class represents a slide in a PowerPoint Document. It allows 
@@ -263,10 +258,85 @@ public class Slide extends Sheet
         return sa.getFollowMasterBackground();
     }
 
-    public Background getBackground() {
+    /**
+     * Sets whether this slide draws master sheet objects
+     *
+     * @param flag  <code>true</code> if the slide draws master sheet objects,
+     * <code>false</code> otherwise
+     */
+    public void setFollowMasterObjects(boolean flag){
+        SlideAtom sa = getSlideRecord().getSlideAtom();
+        sa.setFollowMasterObjects(flag);
+    }
+
+    /**
+     * Whether this slide follows master color scheme
+     *
+     * @return <code>true</code> if the slide follows master color scheme,
+     * <code>false</code> otherwise
+     */
+    public boolean getFollowMasterScheme(){
+        SlideAtom sa = getSlideRecord().getSlideAtom();
+        return sa.getFollowMasterScheme();
+    }
+
+    /**
+     * Sets whether this slide draws master color scheme
+     *
+     * @param flag  <code>true</code> if the slide draws master color scheme,
+     * <code>false</code> otherwise
+     */
+    public void setFollowMasterScheme(boolean flag){
+        SlideAtom sa = getSlideRecord().getSlideAtom();
+        sa.setFollowMasterScheme(flag);
+    }
+
+    /**
+     * Whether this slide draws master sheet objects
+     *
+     * @return <code>true</code> if the slide draws master sheet objects,
+     * <code>false</code> otherwise
+     */
+    public boolean getFollowMasterObjects(){
+        SlideAtom sa = getSlideRecord().getSlideAtom();
+        return sa.getFollowMasterObjects();
+    }
+
+    /**
+     * Background for this slide.
+     */
+     public Background getBackground() {
         if(getFollowMasterBackground())
             return getMasterSheet().getBackground();
         else
             return super.getBackground();
     }
+
+    /**
+     * Color scheme for this slide.
+     */
+    public ColorSchemeAtom getColorScheme() {
+        if(getFollowMasterScheme()){
+            return getMasterSheet().getColorScheme();
+        }
+        return super.getColorScheme();
+    }
+
+    public void draw(Graphics2D graphics){
+        MasterSheet master = getMasterSheet();
+        if(getFollowMasterBackground()) master.getBackground().draw(graphics);
+        if(getFollowMasterObjects()){
+            Shape[] sh = master.getShapes();
+            for (int i = 0; i < sh.length; i++) {
+                if(MasterSheet.isPlaceholder(sh[i])) continue;
+
+                sh[i].draw(graphics);
+            }
+        }
+        Shape[] sh = getShapes();
+        for (int i = 0; i < sh.length; i++) {
+            sh[i].draw(graphics);
+        }
+    }
+
 }
index e442ae304760c7b70ec7b44e9c150b2b02f6d572..26870dbdbbbdeaf9a81cf1ed7bb7152a640f9f83 100644 (file)
@@ -21,12 +21,6 @@ import org.apache.poi.hslf.model.textproperties.TextProp;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.usermodel.SlideShow;
-import org.apache.poi.hslf.record.StyleTextPropAtom.*;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherRecord;
-
-import java.util.List;
-import java.util.Iterator;
 
 /**
  * SlideMaster determines the graphics, layout, and formatting for all the slides in a given presentation.
@@ -82,17 +76,31 @@ public class SlideMaster extends MasterSheet {
             if (prop != null) break;
         }
         if (prop == null) {
-            switch (txtype) {
-                case TextHeaderAtom.CENTRE_BODY_TYPE:
-                case TextHeaderAtom.HALF_BODY_TYPE:
-                case TextHeaderAtom.QUARTER_BODY_TYPE:
-                    txtype = TextHeaderAtom.BODY_TYPE;
-                    break;
-                case TextHeaderAtom.CENTER_TITLE_TYPE:
-                    txtype = TextHeaderAtom.TITLE_TYPE;
-                    break;
-                default:
-                    return null;
+            if(isCharacter) {
+                switch (txtype) {
+                    case TextHeaderAtom.CENTRE_BODY_TYPE:
+                    case TextHeaderAtom.HALF_BODY_TYPE:
+                    case TextHeaderAtom.QUARTER_BODY_TYPE:
+                        txtype = TextHeaderAtom.BODY_TYPE;
+                        break;
+                    case TextHeaderAtom.CENTER_TITLE_TYPE:
+                        txtype = TextHeaderAtom.TITLE_TYPE;
+                        break;
+                    default:
+                        return null;
+                }
+            } else {
+                switch (txtype) {
+                    case TextHeaderAtom.CENTRE_BODY_TYPE:
+                    case TextHeaderAtom.QUARTER_BODY_TYPE:
+                        txtype = TextHeaderAtom.BODY_TYPE;
+                        break;
+                    case TextHeaderAtom.CENTER_TITLE_TYPE:
+                        txtype = TextHeaderAtom.TITLE_TYPE;
+                        break;
+                    default:
+                        return null;
+                }
             }
             prop = getStyleAttribute(txtype, level, name, isCharacter);
         }
@@ -119,4 +127,34 @@ public class SlideMaster extends MasterSheet {
             }
         }
     }
+
+    /**
+     * Checks if the shape is a placeholder.
+     * (placeholders aren't normal shapes, they are visible only in the Edit Master mode)
+     *
+     *
+     * @return true if the shape is a placeholder
+     */
+    public static boolean isPlaceholder(Shape shape){
+        if(!(shape instanceof TextShape)) return false;
+
+        TextShape tx = (TextShape)shape;
+        TextRun run = tx.getTextRun();
+        if(run == null) return false;
+
+        Record[] records = run._records;
+        for (int i = 0; i < records.length; i++) {
+            int type = (int)records[i].getRecordType();
+            if (type == RecordTypes.OEPlaceholderAtom.typeID ||
+                    type == RecordTypes.SlideNumberMCAtom.typeID ||
+                    type == RecordTypes.DateTimeMCAtom.typeID ||
+                    type == RecordTypes.GenericDateMCAtom.typeID ||
+                    type == RecordTypes.FooterMCAtom.typeID ){
+                return true;
+
+            }
+
+        }
+        return false;
+    }
 }
index 947c41af63e06e61aae0f7b4dc4623088a2eb5e4..43dab58af129ef7f5bafe6354778ede79df27796 100755 (executable)
@@ -23,7 +23,6 @@ import org.apache.poi.util.LittleEndian;
 import java.util.*;\r
 import java.util.List;\r
 import java.awt.*;\r
-import java.awt.geom.Rectangle2D;\r
 \r
 /**\r
  * Represents a table in a PowerPoint presentation\r
@@ -113,6 +112,8 @@ public class Table extends ShapeGroup {
     }\r
 \r
     protected void afterInsert(Sheet sh){\r
+        super.afterInsert(sh);\r
+\r
         EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0);\r
         List lst = spCont.getChildRecords();\r
         EscherOptRecord opt = (EscherOptRecord)lst.get(lst.size()-2);\r
index 36789ad91a2f0462c52b0e95311c9c84e1d400dc..7fa69b1d0c2a31c8fdc92e19dc944405b1de8925 100755 (executable)
@@ -18,9 +18,7 @@
 package org.apache.poi.hslf.model;\r
 \r
 import org.apache.poi.ddf.*;\r
-import org.apache.poi.hslf.record.EscherTextboxWrapper;\r
 import org.apache.poi.hslf.record.TextHeaderAtom;\r
-import org.apache.poi.hslf.usermodel.RichTextRun;\r
 \r
 import java.awt.*;\r
 \r
index a1d45ffbaed9ad44678541b769249b2cfedef6e2..dda55a2c7b197da6fd778ad125da32b836b49f01 100644 (file)
 package org.apache.poi.hslf.model;
 
 import org.apache.poi.ddf.*;
-import org.apache.poi.hslf.record.*;
-import org.apache.poi.hslf.usermodel.RichTextRun;
-import org.apache.poi.hslf.exceptions.HSLFException;
-import org.apache.poi.util.POILogger;
-
-import java.awt.*;
-import java.awt.font.FontRenderContext;
-import java.awt.font.TextLayout;
-import java.io.IOException;
 
 /**
  * Represents a TextFrame shape in PowerPoint.
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java
new file mode 100755 (executable)
index 0000000..c1eadc3
--- /dev/null
@@ -0,0 +1,253 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.hslf.usermodel.RichTextRun;\r
+import org.apache.poi.util.POILogger;\r
+import org.apache.poi.util.POILogFactory;\r
+\r
+import java.text.AttributedString;\r
+import java.text.AttributedCharacterIterator;\r
+import java.awt.font.TextAttribute;\r
+import java.awt.font.LineBreakMeasurer;\r
+import java.awt.font.TextLayout;\r
+import java.awt.*;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.geom.Point2D;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * Paint text into java.awt.Graphics2D\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class TextPainter {\r
+    protected POILogger logger = POILogFactory.getLogger(this.getClass());\r
+\r
+    protected TextShape _shape;\r
+\r
+    public TextPainter(TextShape shape){\r
+        _shape = shape;\r
+    }\r
+\r
+    /**\r
+     * Convert the underlying set of rich text runs into java.text.AttributedString\r
+     */\r
+    public AttributedString getAttributedString(TextRun txrun){\r
+        String text = txrun.getText();\r
+        AttributedString at = new AttributedString(text);\r
+        RichTextRun[] rt = txrun.getRichTextRuns();\r
+        for (int i = 0; i < rt.length; i++) {\r
+            int start = rt[i].getStartIndex();\r
+            int end = rt[i].getEndIndex();\r
+            if(start == end) {\r
+                logger.log(POILogger.INFO,  "Skipping RichTextRun with zero length");\r
+                continue;\r
+            }\r
+\r
+            at.addAttribute(TextAttribute.FAMILY, rt[i].getFontName(), start, end);\r
+            at.addAttribute(TextAttribute.SIZE, new Float(rt[i].getFontSize()), start, end);\r
+            at.addAttribute(TextAttribute.FOREGROUND, rt[i].getFontColor(), start, end);\r
+            if(rt[i].isBold()) at.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, start, end);\r
+            if(rt[i].isItalic()) at.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, start, end);\r
+            if(rt[i].isUnderlined()) {\r
+                at.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, start, end);\r
+                at.addAttribute(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, start, end);\r
+            }\r
+            if(rt[i].isStrikethrough()) at.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, start, end);\r
+            int superScript = rt[i].getSuperscript();\r
+            if(superScript != 0) at.addAttribute(TextAttribute.SUPERSCRIPT, superScript > 0 ? TextAttribute.SUPERSCRIPT_SUPER : TextAttribute.SUPERSCRIPT_SUB, start, end);\r
+\r
+        }\r
+        return at;\r
+    }\r
+\r
+    public void paint(Graphics2D graphics){\r
+        TextRun run = _shape.getTextRun();\r
+        if (run == null) return;\r
+\r
+        String text = run.getText();\r
+        if (text == null || text.equals("")) return;\r
+\r
+        AttributedString at = getAttributedString(run);\r
+\r
+        AttributedCharacterIterator it = at.getIterator();\r
+        int paragraphStart = it.getBeginIndex();\r
+        int paragraphEnd = it.getEndIndex();\r
+\r
+        Rectangle2D anchor = _shape.getLogicalAnchor2D();\r
+\r
+        float textHeight = 0;\r
+        ArrayList lines = new ArrayList();\r
+        LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());\r
+        measurer.setPosition(paragraphStart);\r
+        while (measurer.getPosition() < paragraphEnd) {\r
+            int startIndex = measurer.getPosition();\r
+            int nextBreak = text.indexOf('\n', measurer.getPosition() + 1);\r
+\r
+            boolean prStart = text.charAt(startIndex) == '\n';\r
+            if(prStart) measurer.setPosition(startIndex++);\r
+\r
+            RichTextRun rt = run.getRichTextRunAt(startIndex == text.length() ? (startIndex-1) : startIndex);\r
+            if(rt == null) {\r
+                logger.log(POILogger.WARN,  "RichTextRun not found at pos" + startIndex + "; text.length: " + text.length());\r
+                break;\r
+            }\r
+\r
+            float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight();\r
+            wrappingWidth -= rt.getTextOffset();\r
+\r
+            if (_shape.getWordWrap() == TextShape.WrapNone) {\r
+                wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width;\r
+            }\r
+\r
+            TextLayout textLayout = measurer.nextLayout(wrappingWidth + 1,\r
+                    nextBreak == -1 ? paragraphEnd : nextBreak, true);\r
+            if (textLayout == null) {\r
+                textLayout = measurer.nextLayout((float)anchor.getWidth(),\r
+                    nextBreak == -1 ? paragraphEnd : nextBreak, false);\r
+            }\r
+            if(textLayout == null){\r
+                logger.log(POILogger.WARN, "Failed to break text into lines: wrappingWidth: "+wrappingWidth+\r
+                        "; text: " + rt.getText());\r
+                measurer.setPosition(rt.getEndIndex());\r
+                continue;\r
+            }\r
+            int endIndex = measurer.getPosition();\r
+\r
+            float lineHeight = (float)textLayout.getBounds().getHeight();\r
+            int linespacing = rt.getLineSpacing();\r
+            if(linespacing == 0) linespacing = 100;\r
+\r
+            TextElement el = new TextElement();\r
+            if(linespacing >= 0){\r
+                el.ascent = textLayout.getAscent()*linespacing/100;\r
+            } else {\r
+                el.ascent = -linespacing*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+            }\r
+\r
+            el._align = rt.getAlignment();\r
+            el._text = textLayout;\r
+            el._textOffset = rt.getTextOffset();\r
+\r
+            if (prStart){\r
+                int sp = rt.getSpaceBefore();\r
+                float spaceBefore;\r
+                if(sp >= 0){\r
+                    spaceBefore = lineHeight * sp/100;\r
+                } else {\r
+                    spaceBefore = -sp*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+                }\r
+                el.ascent += spaceBefore;\r
+            }\r
+\r
+            float descent;\r
+            if(linespacing >= 0){\r
+                descent = (textLayout.getDescent() + textLayout.getLeading())*linespacing/100;\r
+            } else {\r
+                descent = -linespacing*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+            }\r
+            if (prStart){\r
+                int sp = rt.getSpaceAfter();\r
+                float spaceAfter;\r
+                if(sp >= 0){\r
+                    spaceAfter = lineHeight * sp/100;\r
+                } else {\r
+                    spaceAfter = -sp*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+                }\r
+                el.ascent += spaceAfter;\r
+            }\r
+            el.descent = descent;\r
+\r
+            textHeight += el.ascent + el.descent;\r
+\r
+            if(rt.isBullet() && (prStart || startIndex == 0)){\r
+                it.setIndex(startIndex);\r
+\r
+                AttributedString bat = new AttributedString(Character.toString(rt.getBulletChar()));\r
+                Color clr = rt.getBulletColor();\r
+                if (clr != null) bat.addAttribute(TextAttribute.FOREGROUND, clr);\r
+                else bat.addAttribute(TextAttribute.FOREGROUND, it.getAttribute(TextAttribute.FOREGROUND));\r
+                bat.addAttribute(TextAttribute.FAMILY, it.getAttribute(TextAttribute.FAMILY));\r
+                bat.addAttribute(TextAttribute.SIZE, it.getAttribute(TextAttribute.SIZE));\r
+\r
+                TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext());\r
+                if(text.substring(startIndex, endIndex).length() > 1){\r
+                    el._bullet = bulletLayout;\r
+                    el._bulletOffset = rt.getBulletOffset();\r
+                }\r
+            }\r
+            lines.add(el);\r
+        }\r
+\r
+        int valign = _shape.getVerticalAlignment();\r
+        double y0 = anchor.getY();\r
+        switch (valign){\r
+            case TextShape.AnchorTopBaseline:\r
+            case TextShape.AnchorTop:\r
+                y0 += _shape.getMarginTop();\r
+                break;\r
+            case TextShape.AnchorBottom:\r
+                y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();\r
+                break;\r
+            default:\r
+            case TextShape.AnchorMiddle:\r
+                float delta =  (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();\r
+                y0 += _shape.getMarginTop()  + delta/2;\r
+                break;\r
+        }\r
+\r
+        //finally draw the text fragments\r
+        for (int i = 0; i < lines.size(); i++) {\r
+            TextElement elem = (TextElement)lines.get(i);\r
+            y0 += elem.ascent;\r
+\r
+            Point2D.Double pen = new Point2D.Double();\r
+            pen.y = y0;\r
+            switch (elem._align) {\r
+                default:\r
+                case TextShape.AlignLeft:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft();\r
+                    break;\r
+                case TextShape.AlignCenter:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
+                            (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
+                    break;\r
+                case TextShape.AlignRight:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
+                            (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight());\r
+                    break;\r
+            }\r
+            if(elem._bullet != null){\r
+                elem._bullet.draw(graphics, (float)(pen.x + elem._bulletOffset), (float)pen.y);\r
+            }\r
+            elem._text.draw(graphics, (float)(pen.x + elem._textOffset), (float)pen.y);\r
+\r
+            y0 += elem.descent;\r
+        }\r
+    }\r
+\r
+\r
+    static class TextElement {\r
+        public TextLayout _text;\r
+        public int _textOffset;\r
+        public TextLayout _bullet;\r
+        public int _bulletOffset;\r
+        public int _align;\r
+        public float ascent, descent;\r
+    }\r
+}\r
index 5834e2eaecbf0e9da4960bfc833ca98daea36970..2f77ac5ffa2c044d8796be07c330aedf10563127 100644 (file)
@@ -534,6 +534,10 @@ public class TextRun
                // The messes things up on everything but a Mac, so translate
                //  them to \n
                String text = rawText.replace('\r','\n');
+
+        //0xB acts like cariage return in page titles
+        text = text.replace((char) 0x0B, '\n');
+
                return text;
        }
 
@@ -635,4 +639,20 @@ public class TextRun
     public Hyperlink[] getHyperlinks(){
         return Hyperlink.find(this);
     }
+
+    /**
+     * Fetch RichTextRun at a given position
+     *
+     * @param pos 0-based index in the text
+     * @return RichTextRun or null if not found
+     */
+    public RichTextRun getRichTextRunAt(int pos){
+        for (int i = 0; i < _rtRuns.length; i++) {
+            int start = _rtRuns[i].getStartIndex();
+            int end = _rtRuns[i].getEndIndex();
+            if(pos >= start && pos < end) return _rtRuns[i];
+        }
+        return null;
+    }
+
 }
index 3c45b34b0ba9937ab25adf01a376387b90d1b2d4..7249817bed3b342d5bca22f6e58649c9323189d4 100755 (executable)
@@ -184,6 +184,8 @@ public abstract class TextShape extends SimpleShape {
      * @param sh the sheet we are adding to\r
      */\r
     protected void afterInsert(Sheet sh){\r
+        super.afterInsert(sh);\r
+\r
         EscherTextboxWrapper _txtbox = getEscherTextboxWrapper();\r
         if(_txtbox != null){\r
             PPDrawing ppdrawing = sh.getPPDrawing();\r
@@ -513,4 +515,12 @@ public abstract class TextShape extends SimpleShape {
             }\r
         }\r
     }\r
+\r
+    public void draw(Graphics2D graphics){\r
+        AffineTransform at = graphics.getTransform();\r
+        ShapePainter.paint(this, graphics);\r
+        new TextPainter(this).paint(graphics);\r
+        graphics.setTransform(at);\r
+    }\r
+\r
 }\r
index 7c47427927094576b2f033392fcd7b5078c3d753..197497fd027fe4e8d79fd6b0f514d145753416b7 100644 (file)
@@ -30,7 +30,10 @@ import org.apache.poi.util.LittleEndian;
  *
  * @author Daniel Noll
  */
-public class ExOleObjStg extends RecordAtom {
+public class ExOleObjStg extends RecordAtom implements PersistRecord {
+
+    private int _persistId; // Found from PersistPtrHolder
+
     /**
      * Record header.
      */
@@ -109,4 +112,19 @@ public class ExOleObjStg extends RecordAtom {
         out.write(_header);
         out.write(_data);
     }
+
+    /**
+     * Fetch our sheet ID, as found from a PersistPtrHolder.
+     * Should match the RefId of our matching SlidePersistAtom
+     */
+    public int getPersistId() {
+        return _persistId;
+    }
+
+    /**
+     * Set our sheet ID, as found from a PersistPtrHolder
+     */
+    public void setPersistId(int id) {
+        _persistId = id;
+    }
 }
index 8d25ce2140ded6d2edfea3cde25e8cbb6f2921a1..c7de1fccd7ad1bc8d2803e9e15d552d76b482902 100644 (file)
@@ -37,6 +37,8 @@ public class PPDrawingGroup extends RecordAtom {
 
     private byte[] _header;
     private EscherContainerRecord dggContainer;
+    //cached dgg
+    private EscherDggRecord dgg;
 
     protected PPDrawingGroup(byte[] source, int start, int len) {
         // Get the header
@@ -116,4 +118,17 @@ public class PPDrawingGroup extends RecordAtom {
     public EscherContainerRecord getDggContainer(){
         return dggContainer;
     }
+
+    public EscherDggRecord getEscherDggRecord(){
+        if(dgg == null){
+            for(Iterator it = dggContainer.getChildRecords().iterator(); it.hasNext();){
+                EscherRecord r = (EscherRecord) it.next();
+                if(r instanceof EscherDggRecord){
+                    dgg = (EscherDggRecord)r;
+                    break;
+                }
+            }
+        }
+        return dgg;
+    }
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PersistRecord.java b/src/scratchpad/src/org/apache/poi/hslf/record/PersistRecord.java
new file mode 100755 (executable)
index 0000000..8238d10
--- /dev/null
@@ -0,0 +1,37 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+/**\r
+ * A record that can be referenced in PersistPtr storage.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public interface PersistRecord {\r
+\r
+    /**\r
+     * Fetch the persist ID\r
+     */\r
+    public int getPersistId();\r
+\r
+    /**\r
+     * Set the persist ID\r
+     */\r
+    public void setPersistId(int id);\r
+}\r
index a1bc499453b096748d9a7a7156c7f1c4c293e51d..45038f7a287f1a0c557af79ca420ad47fc0f6803 100644 (file)
@@ -70,9 +70,10 @@ public class RecordTypes {
     public static final Type List = new Type(2000,null);
     public static final Type FontCollection = new Type(2005,FontCollection.class);
     public static final Type BookmarkCollection = new Type(2019,null);
+    public static final Type SoundCollection = new Type(2020,SoundCollection.class);
     public static final Type SoundCollAtom = new Type(2021,null);
-    public static final Type Sound = new Type(2022,null);
-    public static final Type SoundData = new Type(2023,null);
+    public static final Type Sound = new Type(2022,Sound.class);
+    public static final Type SoundData = new Type(2023,SoundData.class);
     public static final Type BookmarkSeedAtom = new Type(2025,null);
     public static final Type ColorSchemeAtom = new Type(2032,ColorSchemeAtom.class);
     public static final Type ExObjRefAtom = new Type(3009,null);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Sound.java b/src/scratchpad/src/org/apache/poi/hslf/record/Sound.java
new file mode 100755 (executable)
index 0000000..211a9b0
--- /dev/null
@@ -0,0 +1,136 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.util.POILogger;\r
+\r
+import java.io.OutputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * A container holding information about a sound. It contains:\r
+ * <p>\r
+ * <li>1. CString (4026), Instance 0: Name of sound (e.g. "crash")\r
+ * <li>2. CString (4026), Instance 1: Type of sound (e.g. ".wav")\r
+ * <li>3. CString (4026), Instance 2: Reference id of sound in sound collection\r
+ * <li>4. CString (4026), Instance 3, optional: Built-in id of sound, for sounds we ship. This is the id that?s in the reg file.\r
+ * <li>5. SoundData (2023), optional\r
+ * </p>\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class Sound extends RecordContainer {\r
+    /**\r
+     * Record header data.\r
+     */\r
+    private byte[] _header;\r
+\r
+    // Links to our more interesting children\r
+    private CString _name;\r
+    private CString _type;\r
+    private SoundData _data;\r
+\r
+\r
+    /**\r
+     * Set things up, and find our more interesting children\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected Sound(byte[] source, int start, int len) {\r
+        // Grab the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Find our children\r
+        _children = Record.findChildRecords(source,start+8,len-8);\r
+        findInterestingChildren();\r
+    }\r
+\r
+    private void findInterestingChildren() {\r
+        // First child should be the ExHyperlinkAtom\r
+        if(_children[0] instanceof CString) {\r
+            _name = (CString)_children[0];\r
+        } else {\r
+            logger.log(POILogger.ERROR, "First child record wasn't a CString, was of type " + _children[0].getRecordType());\r
+        }\r
+\r
+        // Second child should be the ExOleObjAtom\r
+        if (_children[1] instanceof CString) {\r
+            _type = (CString)_children[1];\r
+        } else {\r
+            logger.log(POILogger.ERROR, "Second child record wasn't a CString, was of type " + _children[1].getRecordType());\r
+        }\r
+\r
+        for (int i = 2; i < _children.length; i++) {\r
+            if(_children[i] instanceof SoundData){\r
+                _data = (SoundData)_children[i];\r
+                break;\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    /**\r
+     * Returns the type (held as a little endian in bytes 3 and 4)\r
+     * that this class handles.\r
+     *\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.Sound.typeID;\r
+    }\r
+\r
+    /**\r
+     * Have the contents printer out into an OutputStream, used when\r
+     * writing a file back out to disk.\r
+     *\r
+     * @param out the output stream.\r
+     * @throws java.io.IOException if there was an error writing to the stream.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        writeOut(_header[0],_header[1],getRecordType(),_children,out);\r
+    }\r
+\r
+    /**\r
+     * Name of the sound (e.g. "crash")\r
+     *\r
+     * @return name of the sound\r
+     */\r
+    public String getSoundName(){\r
+        return _name.getText();\r
+    }\r
+\r
+    /**\r
+     * Type of the sound (e.g. ".wav")\r
+     *\r
+     * @return type of the sound\r
+     */\r
+    public String getSoundType(){\r
+        return _type.getText();\r
+    }\r
+\r
+    /**\r
+     * The sound data\r
+     *\r
+     * @return the sound data.\r
+     */\r
+    public byte[] getSoundData(){\r
+        return _data == null ? null : _data.getData();\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SoundCollection.java b/src/scratchpad/src/org/apache/poi/hslf/record/SoundCollection.java
new file mode 100755 (executable)
index 0000000..c244859
--- /dev/null
@@ -0,0 +1,73 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.util.POILogger;\r
+\r
+import java.io.OutputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Is a container for all sound related atoms and containers. It contains:\r
+ *<li>1. SoundCollAtom (2021)\r
+ *<li>2. Sound (2022), for each sound, if any\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class SoundCollection extends RecordContainer {\r
+    /**\r
+     * Record header data.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * Set things up, and find our more interesting children\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected SoundCollection(byte[] source, int start, int len) {\r
+        // Grab the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Find our children\r
+        _children = Record.findChildRecords(source,start+8,len-8);\r
+    }\r
+\r
+    /**\r
+     * Returns the type (held as a little endian in bytes 3 and 4)\r
+     * that this class handles.\r
+     *\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.SoundCollection.typeID;\r
+    }\r
+\r
+    /**\r
+     * Have the contents printer out into an OutputStream, used when\r
+     * writing a file back out to disk.\r
+     *\r
+     * @param out the output stream.\r
+     * @throws java.io.IOException if there was an error writing to the stream.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        writeOut(_header[0],_header[1],getRecordType(),_children,out);\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java b/src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java
new file mode 100755 (executable)
index 0000000..42be48f
--- /dev/null
@@ -0,0 +1,103 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.zip.InflaterInputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * Storage for embedded sounds.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class SoundData extends RecordAtom {\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * Record data.\r
+     */\r
+    private byte[] _data;\r
+\r
+    /**\r
+     * Constructs a new empty sound container.\r
+     */\r
+    protected SoundData() {\r
+        _header = new byte[8];\r
+        _data = new byte[0];\r
+\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _data.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the link related atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected SoundData(byte[] source, int start, int len) {\r
+        // Get the header.\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Get the record data.\r
+        _data = new byte[len-8];\r
+        System.arraycopy(source,start+8,_data,0,len-8);\r
+    }\r
+\r
+    /**\r
+     * Returns the sound data.\r
+     *\r
+     * @return the sound data \r
+     */\r
+    public byte[] getData() {\r
+        return _data;\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     *\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.SoundData.typeID;\r
+    }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk.\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_data);\r
+    }\r
+}\r
index fdaa9eec2c41f3a3134466bf430638ced4d42fb6..187dec12a12f8b578d7e4fb27240cd67f37e8cfe 100644 (file)
@@ -150,7 +150,7 @@ public class StyleTextPropAtom extends RecordAtom
                 new TextProp(2, 0x800000, "symbol"),
                                new TextProp(2, 0x20000, "font.size"),
                                new TextProp(4, 0x40000, "font.color"),
-                               new TextProp(2, 0x80000, "offset"),
+                               new TextProp(2, 0x80000, "superscript"),
                                new TextProp(2, 0x100000, "char_unknown_1"),
                                new TextProp(2, 0x1000000, "char_unknown_3"),
                                new TextProp(2, 0x2000000, "char_unknown_4"),
index 66b4f0373a8948dfb49fd58bf679fac34c1d6b08..957e78868ed6345a1fcc8769a69d865dc3bfac56 100644 (file)
@@ -48,4 +48,13 @@ public class ObjectData {
     public InputStream getData() {
         return storage.getData();
     }
+
+    /**
+     * Return the record that contains the object data.
+     *
+     * @return the record that contains the object data.
+     */
+    public ExOleObjStg getExOleObjStg() {
+        return storage;
+    }
 }
index b250285ac91a3b4e2ad55ca70a2af78d07e13b93..2a09f2224c3dfa9a4103981838b76ab431215c77 100644 (file)
@@ -37,9 +37,6 @@ import org.apache.poi.hslf.record.ColorSchemeAtom;
 /**
  * Represents a run of text, all with the same style
  * 
- * TODO: finish all the getters and setters to the
- *  font/character/paragraph properties (currently only
- *  has some of them) 
  */
 public class RichTextRun {
        /** The TextRun we belong to */
@@ -125,7 +122,25 @@ public class RichTextRun {
        public int getLength() {
                return length;
        }
-       
+
+    /**
+     * The beginning index, inclusive.
+     *
+     * @return the beginning index, inclusive.
+     */
+    public int getStartIndex(){
+        return startPos;
+    }
+
+    /**
+     *  The ending index, exclusive.
+     *
+     * @return the ending index, exclusive.
+     */
+    public int getEndIndex(){
+        return startPos + length;
+    }
+
        /**
         * Fetch the text, in output suitable form
         */
@@ -313,35 +328,143 @@ public class RichTextRun {
        
        
        // --------------- Friendly getters / setters on rich text properties -------
-       
+
+    /**
+     * Is the text bold?
+     */
        public boolean isBold() {
                return isCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX);
        }
+
+    /**
+     * Is the text bold?
+     */
        public void setBold(boolean bold) {
                setCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX, bold);
        }
        
+    /**
+     * Is the text italic?
+     */
        public boolean isItalic() {
                return isCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX);
        }
+
+    /**
+     * Is the text italic?
+     */
        public void setItalic(boolean italic) {
                setCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX, italic);
        }
        
+    /**
+     * Is the text underlined?
+     */
        public boolean isUnderlined() {
                return isCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX);
        }
+
+    /**
+     * Is the text underlined?
+     */
        public void setUnderlined(boolean underlined) {
                setCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX, underlined);
        }
-       
+
+    /**
+     * Does the text have a shadow?
+     */
+    public boolean isShadowed() {
+        return isCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX);
+    }
+
+    /**
+     * Does the text have a shadow?
+     */
+    public void setShadowed(boolean flag) {
+        setCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX, flag);
+    }
+
+    /**
+     * Is this text embossed?
+     */
+     public boolean isEmbossed() {
+        return isCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX);
+    }
+
+    /**
+     * Is this text embossed?
+     */
+     public void setEmbossed(boolean flag) {
+        setCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX, flag);
+    }
+
+    /**
+     * Gets the strikethrough flag
+     */
+    public boolean isStrikethrough() {
+        return isCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX);
+    }
+
+    /**
+     * Sets the strikethrough flag
+     */
+    public void setStrikethrough(boolean flag) {
+        setCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX, flag);
+    }
+
+    /**
+     * Gets the subscript/superscript option
+     *
+     * @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
+     */
+       public int getSuperscript() {
+        int val = getCharTextPropVal("superscript");
+               return val == -1 ? 0 : val;
+       }
+
+    /**
+     * Sets the subscript/superscript option
+     *
+     * @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
+     */
+       public void setSuperscript(int val) {
+               setCharTextPropVal("superscript", val);
+       }
+
+    /**
+     * Gets the font size
+     */
        public int getFontSize() {
                return getCharTextPropVal("font.size");
        }
+
+
+    /**
+     * Sets the font size
+     */
        public void setFontSize(int fontSize) {
                setCharTextPropVal("font.size", fontSize);
        }
-       
+
+    /**
+     * Gets the font index
+     */
+       public int getFontIndex() {
+               return getCharTextPropVal("font.index");
+       }
+
+    /**
+     * Sets the font index
+     */
+       public void setFontIndex(int idx) {
+               setCharTextPropVal("font.index", idx);
+       }
+
+
+    /**
+     * Sets the font name to use
+     */
        public void setFontName(String fontName) {
         if (slideShow == null) {
             //we can't set font since slideshow is not assigned yet
@@ -352,6 +475,10 @@ public class RichTextRun {
                    setCharTextPropVal("font.index", fontIdx);
         }
        }
+
+    /**
+     * Gets the font name
+     */
        public String getFontName() {
         if (slideShow == null) {
             return _fontname;
@@ -368,12 +495,12 @@ public class RichTextRun {
         */
        public Color getFontColor() {
         int rgb = getCharTextPropVal("font.color");
-        if (rgb >= 0x8000000) {
-            int idx = rgb % 0x8000000;
+
+        int cidx = rgb >> 24;
+        if (rgb % 0x1000000 == 0){
             ColorSchemeAtom ca = parentRun.getSheet().getColorScheme();
-            if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx);
+            if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx);
         }
-
         Color tmp = new Color(rgb, true);
         return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
        }
@@ -427,7 +554,7 @@ public class RichTextRun {
     /**
      * Sets indentation level
      *
-     * @param level indentation level. Must be in the range [0, 5]
+     * @param level indentation level. Must be in the range [0, 4]
      */
     public void setIndentLevel(int level) {
         if(paragraphStyle != null ) paragraphStyle.setReservedField((short)level);
@@ -488,6 +615,133 @@ public class RichTextRun {
     public int getTextOffset() {
         return getParaTextPropVal("text.offset")*Shape.POINT_DPI/Shape.MASTER_DPI;
     }
+
+    /**
+     * Sets the bullet size
+     */
+    public void setBulletSize(int size) {
+        setParaTextPropVal("bullet.size", size);
+    }
+
+    /**
+     * Returns the bullet size
+     */
+    public int getBulletSize() {
+        return getParaTextPropVal("bullet.size");
+    }
+
+    /**
+     * Sets the bullet color
+     */
+    public void setBulletColor(Color color) {
+        int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB();
+        setParaTextPropVal("bullet.color", rgb);
+    }
+
+    /**
+     * Returns the bullet color
+     */
+    public Color getBulletColor() {
+        int rgb = getParaTextPropVal("bullet.color");
+        if(rgb == -1) return getFontColor();
+
+        int cidx = rgb >> 24;
+        if (rgb % 0x1000000 == 0){
+            ColorSchemeAtom ca = parentRun.getSheet().getColorScheme();
+            if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx);
+        }
+        Color tmp = new Color(rgb, true);
+        return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
+    }
+
+    /**
+     * Sets the bullet font
+     */
+    public void setBulletFont(int idx) {
+        setParaTextPropVal("bullet.font", idx);
+    }
+
+    /**
+     * Returns the bullet font
+     */
+    public int getBulletFont() {
+        return getParaTextPropVal("bullet.font");
+    }
+
+    /**
+     * Sets the line spacing.
+     * <p>
+     * If linespacing >= 0, then linespacing is a percentage of normal line height.
+     * If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates.
+     * </p>
+     */
+    public void setLineSpacing(int val) {
+        setParaTextPropVal("linespacing", val);
+    }
+
+    /**
+     * Returns the line spacing
+     * <p>
+     * If linespacing >= 0, then linespacing is a percentage of normal line height.
+     * If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates.
+     * </p>
+     *
+     * @return the spacing between lines
+     */
+    public int getLineSpacing() {
+        int val = getParaTextPropVal("linespacing");
+        return val == -1 ? 0 : val;
+    }
+
+    /**
+     * Sets spacing before a paragraph.
+     * <p>
+     * If spacebefore >= 0, then spacebefore is a percentage of normal line height.
+     * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.
+     * </p>
+     */
+    public void setSpaceBefore(int val) {
+        setParaTextPropVal("spacebefore", val);
+    }
+
+    /**
+     * Returns spacing before a paragraph
+     * <p>
+     * If spacebefore >= 0, then spacebefore is a percentage of normal line height.
+     * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.
+     * </p>
+     *
+     * @return the spacing before a paragraph
+     */
+    public int getSpaceBefore() {
+        int val = getParaTextPropVal("spacebefore");
+        return val == -1 ? 0 : val;
+    }
+
+    /**
+     * Sets spacing after a paragraph.
+     * <p>
+     * If spaceafter >= 0, then spaceafter is a percentage of normal line height.
+     * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.
+     * </p>
+     */
+    public void setSpaceAfter(int val) {
+        setParaTextPropVal("spaceafter", val);
+    }
+
+    /**
+     * Returns spacing after a paragraph
+     * <p>
+     * If spaceafter >= 0, then spaceafter is a percentage of normal line height.
+     * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.
+     * </p>
+     *
+     * @return the spacing before a paragraph
+     */
+    public int getSpaceAfter() {
+        int val = getParaTextPropVal("spaceafter");
+        return val == -1 ? 0 : val;
+    }
        // --------------- Internal HSLF methods, not intended for end-user use! -------
        
        /**
index 21e30ac2a9328f655408e72c094082ad60602c55..828255087bf6606764b96cdf20b4d65502348221 100644 (file)
@@ -487,7 +487,21 @@ public class SlideShow
        public PictureData[] getPictureData() {
                return _hslfSlideShow.getPictures();
        }
-       
+
+    /**
+     * Returns the data of all the embedded OLE object in the SlideShow
+     */
+    public ObjectData[] getEmbeddedObjects() {
+        return _hslfSlideShow.getEmbeddedObjects();
+    }
+
+    /**
+     * Returns the data of all the embedded sounds in the SlideShow
+     */
+    public SoundData[] getSoundData() {
+        return SoundData.find(_documentRecord);
+    }
+
        /**
         * Return the current page size
         */
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SoundData.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SoundData.java
new file mode 100755 (executable)
index 0000000..ad7922e
--- /dev/null
@@ -0,0 +1,93 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.usermodel;\r
+\r
+import org.apache.poi.hslf.record.*;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * A class that represents sound data embedded in a slide show.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class SoundData {\r
+    /**\r
+     * The record that contains the object data.\r
+     */\r
+    private Sound _container;\r
+\r
+    /**\r
+     * Creates the object data wrapping the record that contains the sound data.\r
+     *\r
+     * @param container the record that contains the sound data.\r
+     */\r
+    public SoundData(Sound container) {\r
+        this._container = container;\r
+    }\r
+\r
+    /**\r
+     * Name of the sound (e.g. "crash")\r
+     *\r
+     * @return name of the sound\r
+     */\r
+    public String getSoundName(){\r
+        return _container.getSoundName();\r
+    }\r
+\r
+    /**\r
+     * Type of the sound (e.g. ".wav")\r
+     *\r
+     * @return type of the sound\r
+     */\r
+    public String getSoundType(){\r
+        return _container.getSoundType();\r
+    }\r
+\r
+    /**\r
+     * Gets an input stream which returns the binary of the sound data.\r
+     *\r
+     * @return the input stream which will contain the binary of the sound data.\r
+     */\r
+    public byte[] getData() {\r
+        return _container.getSoundData();\r
+    }\r
+\r
+    /**\r
+     * Find all sound records in the supplied Document records\r
+     *\r
+     * @param document the document to find in\r
+     * @return the array with the sound data\r
+     */\r
+    public static SoundData[] find(Document document){\r
+        ArrayList lst = new ArrayList();\r
+        Record[] ch = document.getChildRecords();\r
+        for (int i = 0; i < ch.length; i++) {\r
+            if(ch[i].getRecordType() == RecordTypes.SoundCollection.typeID){\r
+                RecordContainer col = (RecordContainer)ch[i];\r
+                Record[] sr = col.getChildRecords();\r
+                for (int j = 0; j < sr.length; j++) {\r
+                    if(sr[j] instanceof Sound){\r
+                        lst.add(new SoundData((Sound)sr[j]));\r
+                    }\r
+                }\r
+            }\r
+\r
+        }\r
+        return (SoundData[])lst.toArray(new SoundData[lst.size()]);\r
+    }\r
+}\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/41071.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/41071.ppt
new file mode 100755 (executable)
index 0000000..4a44302
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/41071.ppt differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/ringin.wav b/src/scratchpad/testcases/org/apache/poi/hslf/data/ringin.wav
new file mode 100755 (executable)
index 0000000..f8d4292
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/ringin.wav differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/sound.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/sound.ppt
new file mode 100755 (executable)
index 0000000..2d1316a
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/sound.ppt differ
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 (executable)
index 0000000..5e7e7cc
--- /dev/null
@@ -0,0 +1,80 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import junit.framework.TestCase;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.usermodel.RichTextRun;\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+\r
+import java.awt.*;\r
+import java.awt.Rectangle;\r
+import java.awt.geom.*;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.ByteArrayInputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * Test Freeform object.\r
+ * The Freeform shape is constructed from java.awt.GeneralPath.\r
+ * Check that the get/set path accessors are consistent.\r
+ * (TODO: verification of Bezier curves is more difficult due to rounding error.  Figure out a test approach for that)\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestFreeform extends TestCase {\r
+\r
+    public void testClosedPath() throws Exception {\r
+\r
+        GeneralPath path1 = new GeneralPath();\r
+        path1.moveTo(100, 100);\r
+        path1.lineTo(200, 100);\r
+        path1.lineTo(200, 200);\r
+        path1.lineTo(100, 200);\r
+        path1.closePath();\r
+\r
+        Freeform p = new Freeform();\r
+        p.setPath(path1);\r
+\r
+        GeneralPath path2 = p.getPath();\r
+        assertTrue(new Area(path1).equals(new Area(path2)));\r
+    }\r
+\r
+    public void testLine() throws Exception {\r
+\r
+        GeneralPath path1 = new GeneralPath(new Line2D.Double(100, 100, 200, 100));\r
+\r
+        Freeform p = new Freeform();\r
+        p.setPath(path1);\r
+\r
+        GeneralPath path2 = p.getPath();\r
+        assertTrue(new Area(path1).equals(new Area(path2)));\r
+    }\r
+\r
+    public void testRectangle() throws Exception {\r
+\r
+        GeneralPath path1 = new GeneralPath(new Rectangle2D.Double(100, 100, 200, 50));\r
+\r
+        Freeform p = new Freeform();\r
+        p.setPath(path1);\r
+\r
+        GeneralPath path2 = p.getPath();\r
+        assertTrue(new Area(path1).equals(new Area(path2)));\r
+    }\r
+}\r
index 7be6f9dc3253a9fcd05d98048556d92237415130..cb177048ba16e3950ad7ab4efa207bfb6e1c67da 100644 (file)
 package org.apache.poi.hslf.model;\r
 \r
 import java.io.*;\r
+import java.util.List;\r
+import java.util.ArrayList;\r
 \r
 import org.apache.poi.hslf.HSLFSlideShow;\r
 import org.apache.poi.hslf.usermodel.ObjectData;\r
 import org.apache.poi.hslf.usermodel.PictureData;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.hssf.usermodel.HSSFSheet;\r
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;\r
+import org.apache.poi.hwpf.HWPFDocument;\r
+import org.apache.poi.hwpf.usermodel.Range;\r
+import org.apache.poi.hwpf.usermodel.Paragraph;\r
 \r
 import junit.framework.TestCase;\r
 \r
@@ -59,4 +68,41 @@ public class TestOleEmbedding extends TestCase
             slideShow.close();\r
         }\r
     }\r
+\r
+    public void testOLEShape() throws Exception {\r
+        String dirname = System.getProperty("HSLF.testdata.path");\r
+        File file = new File(dirname, "ole2-embedding-2003.ppt");\r
+        FileInputStream is = new FileInputStream(file);\r
+        SlideShow ppt = new SlideShow(is);\r
+        is.close();\r
+\r
+        Slide slide = ppt.getSlides()[0];\r
+        Shape[] sh = slide.getShapes();\r
+        int cnt = 0;\r
+        for (int i = 0; i < sh.length; i++) {\r
+            if(sh[i] instanceof OLEShape){\r
+                cnt++;\r
+                OLEShape ole = (OLEShape)sh[i];\r
+                ObjectData data = ole.getObjectData();\r
+                if("Worksheet".equals(ole.getInstanceName())){\r
+                    //Voila! we created a workbook from the embedded OLE data\r
+                    HSSFWorkbook wb = new HSSFWorkbook(data.getData());\r
+                    HSSFSheet sheet = wb.getSheetAt(0);\r
+                    //verify we can access the xls data\r
+                    assertEquals(1, sheet.getRow(0).getCell((short)0).getNumericCellValue(), 0);\r
+                    assertEquals(1, sheet.getRow(1).getCell((short)0).getNumericCellValue(), 0);\r
+                    assertEquals(2, sheet.getRow(2).getCell((short)0).getNumericCellValue(), 0);\r
+                    assertEquals(3, sheet.getRow(3).getCell((short)0).getNumericCellValue(), 0);\r
+                    assertEquals(8, sheet.getRow(5).getCell((short)0).getNumericCellValue(), 0);\r
+                } else if ("Document".equals(ole.getInstanceName())){\r
+                    //creating a HWPF document \r
+                    HWPFDocument doc = new HWPFDocument(data.getData());\r
+                    String txt = doc.getRange().getParagraph(0).text();\r
+                    assertEquals("OLE embedding is thoroughly unremarkable.\r", txt);\r
+                }\r
+            }\r
+\r
+        }\r
+        assertEquals("Expected 2 OLE shapes", 2, cnt);\r
+    }\r
 }\r
index c2e081ee5caa407782fe441800a0f7ba6a040a99..511ef7f7453fc6d27317c0da4ae3f09610b05caf 100644 (file)
@@ -307,4 +307,22 @@ public class TestShapes extends TestCase {
         sl = ppt.getSlides()[0];
         assertEquals("expected 0 shaped in " + file, 0, sl.getShapes().length);
     }
+
+    public void testShapeId() throws IOException {
+        SlideShow ppt = new SlideShow();
+        Slide slide = ppt.createSlide();
+        Shape shape;
+
+        shape = new Line();
+        assertEquals(0, shape.getShapeId());
+        slide.addShape(shape);
+        assertTrue(shape.getShapeId() > 0);
+
+        int shapeId = shape.getShapeId();
+
+        shape = new Line();
+        assertEquals(0, shape.getShapeId());
+        slide.addShape(shape);
+        assertEquals(shapeId + 1, shape.getShapeId());
+    }
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSound.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSound.java
new file mode 100755 (executable)
index 0000000..97a4b7c
--- /dev/null
@@ -0,0 +1,86 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.text.SimpleDateFormat;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.Arrays;\r
+\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+\r
+/**\r
+ * Tests Sound-related records: SoundCollection(2020), Sound(2022) and SoundData(2023)).\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestSound extends TestCase {\r
+       public void testRealFile() throws Exception {\r
+               String cwd = System.getProperty("HSLF.testdata.path");\r
+               FileInputStream is = new FileInputStream(new File(cwd, "sound.ppt"));\r
+               SlideShow ppt = new SlideShow(is);\r
+               is.close();\r
+\r
+               // Get the document\r
+               Document doc = ppt.getDocumentRecord();\r
+        SoundCollection soundCollection = null;\r
+        Record[] doc_ch = doc.getChildRecords();\r
+        for (int i = 0; i < doc_ch.length; i++) {\r
+            if(doc_ch[i] instanceof SoundCollection){\r
+                soundCollection = (SoundCollection)doc_ch[i];\r
+                break;\r
+            }\r
+        }\r
+        assertNotNull(soundCollection);\r
+\r
+        Sound sound = null;\r
+        Record[] sound_ch = soundCollection.getChildRecords();\r
+        int k = 0;\r
+        for (int i = 0; i < sound_ch.length; i++) {\r
+            if(sound_ch[i] instanceof Sound){\r
+                sound = (Sound)sound_ch[i];\r
+                k++;\r
+            }\r
+        }\r
+        assertNotNull(sound);\r
+        assertEquals(1, k);\r
+\r
+        assertEquals("ringin.wav", sound.getSoundName());\r
+        assertEquals(".WAV", sound.getSoundType());\r
+        assertNotNull(sound.getSoundData());\r
+\r
+        File f = new File(cwd, "ringin.wav");\r
+        int length = (int)f.length();\r
+        byte[] ref_data = new byte[length];\r
+        is = new FileInputStream(f);\r
+        is.read(ref_data);\r
+        is.close();\r
+\r
+        assertTrue(Arrays.equals(ref_data, sound.getSoundData()));\r
+\r
+       }\r
+}\r
index 19e7af64177e1119568846757a8dcff3c8031edb..6947ff9cfd7a30d666662e26d93c4111c73c4fd8 100644 (file)
@@ -360,4 +360,24 @@ public class TestBugs extends TestCase {
 \r
         assertTrue("No Exceptions while reading file", true);\r
     }\r
+\r
+    /**\r
+     * Bug 41071: Will not extract text from Powerpoint TextBoxes\r
+     */\r
+    public void test41071() throws Exception {\r
+        FileInputStream is = new FileInputStream(new File(cwd, "41071.ppt"));\r
+        SlideShow ppt = new SlideShow(is);\r
+        is.close();\r
+\r
+        Slide slide = ppt.getSlides()[0];\r
+        Shape[] sh = slide.getShapes();\r
+        assertEquals(1, sh.length);\r
+        assertTrue(sh[0] instanceof TextShape);\r
+        TextShape tx = (TextShape)sh[0];\r
+        assertEquals("Fundera, planera och involvera.", tx.getTextRun().getText());\r
+\r
+        TextRun[] run = slide.getTextRuns();\r
+        assertEquals(1, run.length);\r
+        assertEquals("Fundera, planera och involvera.", run[0].getText());\r
+    }\r
 }\r
index 797f97cda729227636f96f6f546bae8aad8df4e3..78458f0630d291cacef63767dab49d13d90c804b 100644 (file)
@@ -437,4 +437,33 @@ public class TestPictures extends TestCase{
         assertTrue(pdata instanceof WMF);
         assertEquals(Picture.WMF, pdata.getType());
        }
+
+    public void testGetPictureName() throws Exception {
+        SlideShow ppt = new SlideShow(new HSLFSlideShow(new File(cwd, "ppt_with_png.ppt").getPath()));
+        Slide slide = ppt.getSlides()[0];
+
+        Picture p = (Picture)slide.getShapes()[0]; //the first slide contains JPEG
+        assertEquals("test", p.getPictureName());
+    }
+
+    public void testSetPictureName() throws Exception {
+        SlideShow ppt = new SlideShow();
+
+        Slide slide = ppt.createSlide();
+        File img = new File(cwd, "tomcat.png");
+        int idx = ppt.addPicture(img, Picture.PNG);
+        Picture pict = new Picture(idx);
+        pict.setPictureName("tomcat.png");
+        slide.addShape(pict);
+
+        //serialize and read again
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ppt.write(out);
+        out.close();
+
+        ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));
+
+        Picture p = (Picture)ppt.getSlides()[0].getShapes()[0];
+        assertEquals("tomcat.png", p.getPictureName());
+    }
 }
index 3fda0fda17f006d8490ff7b71a742acb85a4cb41..3d89d0272f4f7b15c53b56e9a8fd297aa8591760 100644 (file)
@@ -141,7 +141,7 @@ public class TestSlideOrdering extends TestCase {
                     "ROMANCE: AN ANALYSIS",
                     "AGENDA",
                     "You are an important supplier of various items that I need",
-                    (char)0x0B + "Although The Psycho set back my relationship process, recovery is luckily enough under way",
+                    '\n' + "Although The Psycho set back my relationship process, recovery is luckily enough under way",
                     "Since the time that we seriously go out together, you rank highly among existing relationships",
                     "Although our personal interests are mostly compatible, the greatest gap exists in Sex and Shopping",
                     "Your physical characteristics are strong when compared with your competition",
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestSoundData.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestSoundData.java
new file mode 100755 (executable)
index 0000000..95de38f
--- /dev/null
@@ -0,0 +1,63 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.usermodel;\r
+\r
+import org.apache.poi.hslf.*;\r
+import org.apache.poi.hslf.exceptions.HSLFException;\r
+import org.apache.poi.hslf.blip.*;\r
+import org.apache.poi.hslf.model.*;\r
+import junit.framework.TestCase;\r
+\r
+import java.io.*;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Test reading sound data from a ppt\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestSoundData extends TestCase{\r
+\r
+    protected File cwd;\r
+\r
+    public void setUp() throws Exception {\r
+        cwd = new File(System.getProperty("HSLF.testdata.path"));\r
+    }\r
+\r
+    /**\r
+     * Read a reference sound file from disk and compare it from the data extracted from the slide show\r
+     */ \r
+    public void testSounds() throws Exception {\r
+        //read the reference sound file\r
+        File f = new File(cwd, "ringin.wav");\r
+        int length = (int)f.length();\r
+        byte[] ref_data = new byte[length];\r
+        FileInputStream is = new FileInputStream(f);\r
+        is.read(ref_data);\r
+        is.close();\r
+\r
+        is = new FileInputStream(new File(cwd, "sound.ppt"));\r
+        SlideShow ppt = new SlideShow(is);\r
+        is.close();\r
+\r
+        SoundData[] sound = ppt.getSoundData();\r
+        assertEquals("Expected 1 sound", 1, sound.length);\r
+\r
+        assertTrue(Arrays.equals(ref_data, sound[0].getData()));\r
+    }\r
+}\r
diff --git a/src/testcases/org/apache/poi/hssf/data/28774.xls b/src/testcases/org/apache/poi/hssf/data/28774.xls
new file mode 100755 (executable)
index 0000000..476f3ba
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/28774.xls differ
index 49adad852333bb87dc5af1f08fe4c23d2f3a32e8..259e3d0c86b65244a8aa55c5fc9ed0629e4d00af 100644 (file)
@@ -878,4 +878,15 @@ public final class TestBugs extends TestCase {
         }
         assertEquals(713, rowsSeen);
     }
+
+    /**
+     * Bug 28774: Excel will crash when opening xls-files with images.
+     */
+    public void test28774() {
+
+        HSSFWorkbook wb = openSample("28774.xls");
+        assertTrue("no errors reading sample xls", true);
+        writeOutAndReadBack(wb);
+        assertTrue("no errors writing sample xls", true);
+    }
 }
index f7ce61faf2f9d06c40c388fe9ba9ce76bd1576d8..f836d4284e6404424f21801f2a53450a47ac0b72 100755 (executable)
@@ -49,4 +49,23 @@ public final class TestHSSFTextbox extends TestCase{
         assertEquals(HSSFTextbox.VERTICAL_ALIGNMENT_CENTER, textbox.getVerticalAlignment());\r
     }\r
 \r
+    /**\r
+     * Excel requires at least one format run in HSSFTextbox.\r
+     * When inserting text make sure that if font is not set we must set the default one.\r
+     */\r
+    public void testSetDeafultTextFormat() {\r
+        HSSFWorkbook wb = new HSSFWorkbook();\r
+        HSSFSheet sheet = wb.createSheet();\r
+        HSSFPatriarch patriarch = sheet.createDrawingPatriarch();\r
+\r
+        HSSFTextbox textbox1 = patriarch.createTextbox(new HSSFClientAnchor(0,0,0,0,(short)1,1,(short)3,3));\r
+        HSSFRichTextString rt1 = new HSSFRichTextString("Hello, World!");\r
+        assertEquals(0, rt1.numFormattingRuns());\r
+        textbox1.setString(rt1);\r
+\r
+        HSSFRichTextString rt2 = textbox1.getString();\r
+        assertEquals(1, rt2.numFormattingRuns());\r
+        assertEquals(HSSFRichTextString.NO_FONT, rt2.getFontOfFormattingRun(0));\r
+    }\r
+\r
  }\r