Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-650914,650916-650920 via svnmerge from

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
This commit is contained in:
Nick Burch 2008-04-23 16:49:18 +00:00
parent 25e37453cc
commit 24ba833f03
63 changed files with 3483 additions and 135 deletions

View File

@ -43,7 +43,13 @@
<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 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> <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>
<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="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">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> <action dev="POI-DEVELOPERS" type="fix">Fix the logger used by POIFSFileSystem, so that commons-logging isn't required when not used</action>

View File

@ -136,8 +136,7 @@
</section> </section>
<section><title>HSLF for PowerPoint Documents</title> <section><title>HSLF for PowerPoint Documents</title>
<p>HSLF is our port of the Microsoft PowerPoint 97(-2003) file format to pure <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 Java. It supports read and write capabilities. Please see <link
of the core records. Please see <link
href="./slideshow/index.html">the HSLF project page for more href="./slideshow/index.html">the HSLF project page for more
information</link>.</p> information</link>.</p>
</section> </section>

View File

@ -61,9 +61,9 @@
<section><title>Files embeded in PowerPoint</title> <section><title>Files embeded in PowerPoint</title>
<p>PowerPoint does not normally store embeded files <p>PowerPoint does not normally store embeded files
in the OLE2 layer. Instead, they are held within records in the OLE2 layer. Instead, they are held within records
of the main PowerPoint file. To get at them, you need to of the main PowerPoint file.
find the appropriate data within the PowerPoint stream, <br/>See the <link href="./../hslf/how-to-shapes.html#OLE">HSLF Tutorial</link>
and work from that.</p> for how to retrieve embedded OLE objects from a presentation</p>
</section> </section>
</section> </section>

View File

@ -40,6 +40,12 @@
<li><link href="#Bullets">How to create bulleted lists</link></li> <li><link href="#Bullets">How to create bulleted lists</link></li>
<li><link href="#Hyperlinks">Hyperlinks</link></li> <li><link href="#Hyperlinks">Hyperlinks</link></li>
<li><link href="#Tables">Tables</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> </ul>
</section> </section>
<section><title>Features</title> <section><title>Features</title>
@ -80,14 +86,8 @@
</section> </section>
<anchor id="GetShapes"/> <anchor id="GetShapes"/>
<section><title>How to get shapes contained in a particular slide</title> <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> <p>
<img src="images/hslf_shapes.gif" alt="Class Tree of HSLF Shapes" width="611" height="412"/> The following code demonstrates how to iterate over shapes for each slide.
</p>
<p>
The following fragment demonstrates how to iterate over shapes for each slide.
</p> </p>
<source> <source>
SlideShow ppt = new SlideShow(new HSLFSlideShow("slideshow.ppt")); SlideShow ppt = new SlideShow(new HSLFSlideShow("slideshow.ppt"));
@ -440,6 +440,185 @@
</source> </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>
</section> </section>
</body> </body>

View File

@ -26,6 +26,7 @@
<authors> <authors>
<person name="Avik Sengupta" email="avik at apache dot org"/> <person name="Avik Sengupta" email="avik at apache dot org"/>
<person name="Nick Burch" email="nick 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> </authors>
</header> </header>
@ -37,9 +38,12 @@
Powerpoint '97(-2007) file format. It <em>does not</em> support Powerpoint '97(-2007) file format. It <em>does not</em> support
the new PowerPoint 2007 .pptx file format, which is not OLE2 the new PowerPoint 2007 .pptx file format, which is not OLE2
based.</p> based.</p>
<p>HSLF provides a way to read powerpoint presentations, and extract text from it. <p>HSLF provides a way to read, create or modify PowerPoint presentations. In particular, it provides:
It also provides some (currently limited) edit capabilities.
</p> </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> <note>
This code currently lives the This code currently lives the
<link href="http://svn.apache.org/viewcvs.cgi/poi/trunk/src/scratchpad/">scratchpad area</link> <link href="http://svn.apache.org/viewcvs.cgi/poi/trunk/src/scratchpad/">scratchpad area</link>

View File

@ -40,7 +40,13 @@
<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 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> <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>
<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="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">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> <action dev="POI-DEVELOPERS" type="fix">Fix the logger used by POIFSFileSystem, so that commons-logging isn't required when not used</action>

View File

@ -0,0 +1,135 @@
POI Release Guide
(I) Prerequisites
1. You should read the <a href="http://apache.org/dev/release.html">Apache Release FAQ</a>
2. You must have shell access to people.apache.org
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.
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>
4. You must have <a href="java.sun.com">JDK 1.4 / 1.5</a>
5. You must have the following utilities installed on your local machine and available in your path:
* <a href="www.openssh.com">ssh</a>
* <a href="www.gnupg.org">gnupg</a>
* <a href="www.openssl.org">openssl</a>
For Windows users, install Cygwin and make sure you have the above utilities
6. The POI build system requires two components to perform a build
* <a href="ant.apache.org">Ant</a>
* <a href="http://forrest.apache.org/">Forrest</a>.
POI 3.0.2 and 3.1 were built using Ant 1.6.2 and Forrest 0.5
(II) Making release artefacts
1. Update version id in build.xml.
2. Tag current version. Include the current revision number in the comment
{code}
$ svn cp https://svn.apache.org/repos/asf/poi/trunk \
https://svn.apache.org/repos/asf/poi/tags/$TAG \
-m "tag r649911 as 3.1-beta1"
{code}
where $TAG is the release tag, for example, REL_3_1_BETA1
3. Checkout the tagged version
{code}
cd tags
svn checkout https://svn.apache.org/repos/asf/poi/tags/TAG
{code}
4. Merge (if required)
{code}
cd $TAG
$ svn merge https://svn.apache.org/repos/asf/poi/tags/TAG \
https://svn.apache.org/repos/asf/poi/trunk
{code}
5. Start a new section in sites.xml and status.xml.
6. Build as if the vote had passed. The buid date must be +7 days from current.
{code}
ant build
{code}
After build you should have the following files in the build/dist:
{code}
poi-$TAG-$DATE.jar
poi-bin-$TAG-$DATE.tar.gz
poi-bin-$TAG-$DATE.zip
poi-contrib-$TAG-$DATE.jar
poi-scratchpad-$TAG-$DATE.jar
poi-src-$TAG-$DATE.tar.gz
poi-src-$TAG-$DATE.zip
{code}
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).
7. Build Mavn POM files
{code}
ant maven-dist
{code}
8. Signing the release artifacts:
{code}
cd build/dist
for i in *.zip ; do
gpg --armor --output $i.asc --detach-sig $i;
done
for i in *.gz ; do
gpg --armor --output $i.asc --detach-sig $i;
done
{code}
Verify the signatures:
{code}
gpg --multifile --verify *.asc
{code}
9. Create MD5 checksums for all artifacts to be published:
{code}
for i in *.zip ; do
openssl md5 < $i > $i.md5
done
for i in *.gz ; do
openssl md5 < $i > $i.md5
done
{code}
10. Upload to your area at people.apache.org.
There should be two directories:
main
maven
Make sure that the all files have read permission.
(III) After the vote:
Log-in on people.apache.org
1. Go to ~/POI-3.1-BETA1
cd ~/POI-3.1-BETA1/main
BETA and ALPHA releases:
cp *-src-* /www/www.apache.org/dist/poi/dev/src
cp *-bin-* /www/www.apache.org/dist/poi/dev/bin
FINAL release:
cp *-src-* /www/www.apache.org/dist/poi/release/src
cp *-bin-* /www/www.apache.org/dist/poi/release/bin
cd ~/POI-3.1-BETA1/maven
cp -r org.apache.poi /www/people.apache.org/repo/m1-ibiblio-rsync-repository/
cp -r poi/poms /www/people.apache.org/repo/m1-ibiblio-rsync-repository/poi
2. Make sure that the files are owned by the unix group apcvs and that they are writable by this group.
3. Wait for the distributions to appear on your favourite mirror
4. Send announcements:
- to poi-user and poi-dev lists
- send announcements to announcement@apache.org, announcements@jakarta.apache.org

View File

@ -83,7 +83,12 @@ public class HSSFTextbox
*/ */
public void setString( RichTextString string ) 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;
} }
/** /**

View File

@ -0,0 +1,148 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.examples;
import org.apache.poi.hslf.usermodel.*;
import org.apache.poi.hslf.model.*;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.hwpf.usermodel.Paragraph;
import java.io.*;
/**
* Demonstrates how you can extract misc embedded data from a ppt file
*
* @author Yegor Kozlov
*/
public class DataExtraction {
public static void main(String args[]) throws Exception {
if (args.length == 0) {
usage();
return;
}
FileInputStream is = new FileInputStream(args[0]);
SlideShow ppt = new SlideShow(is);
is.close();
//extract all sound files embedded in this presentation
SoundData[] sound = ppt.getSoundData();
for (int i = 0; i < sound.length; i++) {
String type = sound[i].getSoundType(); //*.wav
String name = sound[i].getSoundName(); //typically file name
byte[] data = sound[i].getData(); //raw bytes
//save the sound on disk
FileOutputStream out = new FileOutputStream(name + type);
out.write(data);
out.close();
}
//extract embedded OLE documents
Slide[] slide = ppt.getSlides();
for (int i = 0; i < slide.length; i++) {
Shape[] shape = slide[i].getShapes();
for (int j = 0; j < shape.length; j++) {
if (shape[j] instanceof OLEShape) {
OLEShape ole = (OLEShape) shape[j];
ObjectData data = ole.getObjectData();
String name = ole.getInstanceName();
if ("Worksheet".equals(name)) {
//save xls on disk
FileOutputStream out = new FileOutputStream(name + "-("+(j)+").xls");
InputStream dis = data.getData();
byte[] chunk = new byte[2048];
int count;
while ((count = dis.read(chunk)) >= 0) {
out.write(chunk,0,count);
}
is.close();
out.close();
} else if ("Document".equals(name)) {
HWPFDocument doc = new HWPFDocument(data.getData());
//read the word document
Range r = doc.getRange();
for(int k = 0; k < r.numParagraphs(); k++) {
Paragraph p = r.getParagraph(k);
System.out.println(p.text());
}
//save on disk
FileOutputStream out = new FileOutputStream(name + "-("+(j)+").doc");
doc.write(out);
out.close();
} else {
System.err.println("Processing " + name);
}
}
}
}
//Pictures
for (int i = 0; i < slide.length; i++) {
Shape[] shape = slide[i].getShapes();
for (int j = 0; j < shape.length; j++) {
if (shape[j] instanceof Picture) {
Picture p = (Picture) shape[j];
PictureData data = p.getPictureData();
String name = p.getPictureName();
int type = data.getType();
String ext;
switch (type) {
case Picture.JPEG:
ext = ".jpg";
break;
case Picture.PNG:
ext = ".png";
break;
case Picture.WMF:
ext = ".wmf";
break;
case Picture.EMF:
ext = ".emf";
break;
case Picture.PICT:
ext = ".pict";
break;
case Picture.DIB:
ext = ".dib";
break;
default:
continue;
}
FileOutputStream out = new FileOutputStream("pict-" + j + ext);
out.write(data.getData());
out.close();
}
}
}
}
private static void usage(){
System.out.println("Usage: DataExtraction ppt");
}
}

View File

@ -0,0 +1,80 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.examples;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hslf.model.*;
import java.awt.*;
import java.io.FileOutputStream;
import java.io.FileInputStream;
/**
* Demonstrates how to draw into a slide using the HSLF Graphics2D driver.
*
* @author Yegor Kozlov
*/
public class Graphics2DDemo {
/**
* A simple bar chart demo
*/
public static void main(String[] args) throws Exception {
SlideShow ppt = new SlideShow();
//bar chart data. The first value is the bar color, the second is the width
Object[] def = new Object[]{
Color.yellow, new Integer(40),
Color.green, new Integer(60),
Color.gray, new Integer(30),
Color.red, new Integer(80),
};
Slide slide = ppt.createSlide();
ShapeGroup group = new ShapeGroup();
//define position of the drawing in the slide
Rectangle bounds = new java.awt.Rectangle(200, 100, 350, 300);
group.setAnchor(bounds);
group.setCoordinates(new java.awt.Rectangle(0, 0, 100, 100));
slide.addShape(group);
Graphics2D graphics = new PPGraphics2D(group);
//draw a simple bar graph
int x = 10, y = 10;
graphics.setFont(new Font("Arial", Font.BOLD, 10));
for (int i = 0, idx = 1; i < def.length; i+=2, idx++) {
graphics.setColor(Color.black);
int width = ((Integer)def[i+1]).intValue();
graphics.drawString("Q" + idx, x-5, y+10);
graphics.drawString(width + "%", x + width+3, y + 10);
graphics.setColor((Color)def[i]);
graphics.fill(new Rectangle(x, y, width, 10));
y += 15;
}
graphics.setColor(Color.black);
graphics.setFont(new Font("Arial", Font.BOLD, 14));
graphics.draw(group.getCoordinates());
graphics.drawString("Performance", x + 30, y + 10);
FileOutputStream out = new FileOutputStream("hslf-graphics.ppt");
ppt.write(out);
out.close();
}
}

View File

@ -0,0 +1,108 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.examples;
import org.apache.poi.hslf.usermodel.*;
import org.apache.poi.hslf.model.*;
import org.apache.poi.hslf.record.TextHeaderAtom;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/**
* Demonstrates how you can use HSLF to convert each slide into a PNG image
*
* @author Yegor Kozlov
*/
public class PPT2PNG {
public static void main(String args[]) throws Exception {
if (args.length == 0) {
usage();
return;
}
int slidenum = -1;
float scale = 1;
String file = null;
for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("-")) {
if ("-scale".equals(args[i])){
scale = Float.parseFloat(args[++i]);
} else if ("-slide".equals(args[i])) {
slidenum = Integer.parseInt(args[++i]);
}
} else {
file = args[i];
}
}
if(file == null){
usage();
return;
}
FileInputStream is = new FileInputStream(file);
SlideShow ppt = new SlideShow(is);
is.close();
Dimension pgsize = ppt.getPageSize();
int width = (int)(pgsize.width*scale);
int height = (int)(pgsize.height*scale);
Slide[] slide = ppt.getSlides();
for (int i = 0; i < slide.length; i++) {
if (slidenum != -1 && slidenum != (i+1)) continue;
String title = slide[i].getTitle();
System.out.println("Rendering slide "+slide[i].getSlideNumber() + (title == null ? "" : ": " + title));
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, width, height));
graphics.scale((double)width/pgsize.width, (double)height/pgsize.height);
slide[i].draw(graphics);
String fname = file.replaceAll("\\.ppt", "-" + (i+1) + ".png");
FileOutputStream out = new FileOutputStream(fname);
ImageIO.write(img, "png", out);
out.close();
}
}
private static void usage(){
System.out.println("Usage: PPT2PNG [-scale <scale> -slide <num>] ppt");
}
}

View File

@ -27,22 +27,13 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.POIDocument; import org.apache.poi.POIDocument;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException; import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.record.CurrentUserAtom; import org.apache.poi.hslf.record.*;
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.usermodel.ObjectData; import org.apache.poi.hslf.usermodel.ObjectData;
import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.PictureData;
import org.apache.poi.hslf.model.Shape; import org.apache.poi.hslf.model.Shape;
@ -253,6 +244,7 @@ public class HSLFSlideShow extends POIDocument
private Record[] read(byte[] docstream, int usrOffset){ private Record[] read(byte[] docstream, int usrOffset){
ArrayList lst = new ArrayList(); ArrayList lst = new ArrayList();
HashMap offset2id = new HashMap();
while (usrOffset != 0){ while (usrOffset != 0){
UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset); UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
lst.add(new Integer(usrOffset)); lst.add(new Integer(usrOffset));
@ -266,6 +258,7 @@ public class HSLFSlideShow extends POIDocument
Integer offset = (Integer)entries.get(id); Integer offset = (Integer)entries.get(id);
lst.add(offset); lst.add(offset);
offset2id.put(offset, id);
} }
usrOffset = usr.getLastUserEditAtomOffset(); usrOffset = usr.getLastUserEditAtomOffset();
@ -278,6 +271,11 @@ public class HSLFSlideShow extends POIDocument
for (int i = 0; i < a.length; i++) { for (int i = 0; i < a.length; i++) {
Integer offset = (Integer)a[i]; Integer offset = (Integer)a[i];
rec[i] = (Record)Record.buildRecordAtOffset(docstream, offset.intValue()); 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; return rec;

View File

@ -18,6 +18,9 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.ddf.*; import org.apache.poi.ddf.*;
import org.apache.poi.util.POILogger;
import java.awt.geom.Rectangle2D;
/** /**
* Represents an AutoShape. * Represents an AutoShape.
@ -102,4 +105,17 @@ public class AutoShape extends TextShape {
setEscherProperty((short)(EscherProperties.GEOMETRY__ADJUSTVALUE + idx), val); 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);
}
}
} }

View File

@ -0,0 +1,373 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.model;
import org.apache.poi.ddf.EscherProperties;
import java.awt.geom.*;
/**
* Stores definition of auto-shapes.
* See the Office Drawing 97-2007 Binary Format Specification for details.
*
* TODO: follow the spec and define all the auto-shapes
*
* @author Yegor Kozlov
*/
public class AutoShapes {
protected static ShapeOutline[] shapes;
/**
* Return shape outline by shape type
* @param type shape type see {@link ShapeTypes}
*
* @return the shape outline
*/
public static ShapeOutline getShapeOutline(int type){
ShapeOutline outline = shapes[type];
return outline;
}
/**
* Auto-shapes are defined in the [0,21600] coordinate system.
* We need to transform it into normal slide coordinates
*
*/
public static java.awt.Shape transform(java.awt.Shape outline, Rectangle2D anchor){
AffineTransform at = new AffineTransform();
at.translate(anchor.getX(), anchor.getY());
at.scale(
1.0f/21600*anchor.getWidth(),
1.0f/21600*anchor.getHeight()
);
return at.createTransformedShape(outline);
}
static {
shapes = new ShapeOutline[255];
shapes[ShapeTypes.Rectangle] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
Rectangle2D path = new Rectangle2D.Float(0, 0, 21600, 21600);
return path;
}
};
shapes[ShapeTypes.RoundRectangle] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
RoundRectangle2D path = new RoundRectangle2D.Float(0, 0, 21600, 21600, adjval, adjval);
return path;
}
};
shapes[ShapeTypes.Ellipse] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
Ellipse2D path = new Ellipse2D.Float(0, 0, 21600, 21600);
return path;
}
};
shapes[ShapeTypes.Diamond] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
GeneralPath path = new GeneralPath();
path.moveTo(10800, 0);
path.lineTo(21600, 10800);
path.lineTo(10800, 21600);
path.lineTo(0, 10800);
path.closePath();
return path;
}
};
//m@0,l,21600r21600
shapes[ShapeTypes.IsocelesTriangle] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 10800);
GeneralPath path = new GeneralPath();
path.moveTo(adjval, 0);
path.lineTo(0, 21600);
path.lineTo(21600, 21600);
path.closePath();
return path;
}
};
shapes[ShapeTypes.RightTriangle] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
GeneralPath path = new GeneralPath();
path.moveTo(0, 0);
path.lineTo(21600, 21600);
path.lineTo(0, 21600);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Parallelogram] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(adjval, 0);
path.lineTo(21600, 0);
path.lineTo(21600 - adjval, 21600);
path.lineTo(0, 21600);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Trapezoid] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(0, 0);
path.lineTo(adjval, 21600);
path.lineTo(21600 - adjval, 21600);
path.lineTo(21600, 0);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Hexagon] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(adjval, 0);
path.lineTo(21600 - adjval, 0);
path.lineTo(21600, 10800);
path.lineTo(21600 - adjval, 21600);
path.lineTo(adjval, 21600);
path.lineTo(0, 10800);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Octagon] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 6326);
GeneralPath path = new GeneralPath();
path.moveTo(adjval, 0);
path.lineTo(21600 - adjval, 0);
path.lineTo(21600, adjval);
path.lineTo(21600, 21600-adjval);
path.lineTo(21600-adjval, 21600);
path.lineTo(adjval, 21600);
path.lineTo(0, 21600-adjval);
path.lineTo(0, adjval);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Plus] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(adjval, 0);
path.lineTo(21600 - adjval, 0);
path.lineTo(21600 - adjval, adjval);
path.lineTo(21600, adjval);
path.lineTo(21600, 21600-adjval);
path.lineTo(21600-adjval, 21600-adjval);
path.lineTo(21600-adjval, 21600);
path.lineTo(adjval, 21600);
path.lineTo(adjval, 21600-adjval);
path.lineTo(0, 21600-adjval);
path.lineTo(0, adjval);
path.lineTo(adjval, adjval);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Pentagon] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
GeneralPath path = new GeneralPath();
path.moveTo(10800, 0);
path.lineTo(21600, 8259);
path.lineTo(21600 - 4200, 21600);
path.lineTo(4200, 21600);
path.lineTo(0, 8259);
path.closePath();
return path;
}
};
shapes[ShapeTypes.DownArrow] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
//m0@0 l@1@0 @1,0 @2,0 @2@0,21600@0,10800,21600xe
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 16200);
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(0, adjval);
path.lineTo(adjval2, adjval);
path.lineTo(adjval2, 0);
path.lineTo(21600-adjval2, 0);
path.lineTo(21600-adjval2, adjval);
path.lineTo(21600, adjval);
path.lineTo(10800, 21600);
path.closePath();
return path;
}
};
shapes[ShapeTypes.UpArrow] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
//m0@0 l@1@0 @1,21600@2,21600@2@0,21600@0,10800,xe
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(0, adjval);
path.lineTo(adjval2, adjval);
path.lineTo(adjval2, 21600);
path.lineTo(21600-adjval2, 21600);
path.lineTo(21600-adjval2, adjval);
path.lineTo(21600, adjval);
path.lineTo(10800, 0);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Arrow] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
//m@0, l@0@1 ,0@1,0@2@0@2@0,21600,21600,10800xe
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 16200);
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(adjval, 0);
path.lineTo(adjval, adjval2);
path.lineTo(0, adjval2);
path.lineTo(0, 21600-adjval2);
path.lineTo(adjval, 21600-adjval2);
path.lineTo(adjval, 21600);
path.lineTo(21600, 10800);
path.closePath();
return path;
}
};
shapes[ShapeTypes.LeftArrow] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
//m@0, l@0@1,21600@1,21600@2@0@2@0,21600,,10800xe
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400);
GeneralPath path = new GeneralPath();
path.moveTo(adjval, 0);
path.lineTo(adjval, adjval2);
path.lineTo(21600, adjval2);
path.lineTo(21600, 21600-adjval2);
path.lineTo(adjval, 21600-adjval2);
path.lineTo(adjval, 21600);
path.lineTo(0, 10800);
path.closePath();
return path;
}
};
shapes[ShapeTypes.Can] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
//m10800,qx0@1l0@2qy10800,21600,21600@2l21600@1qy10800,xem0@1qy10800@0,21600@1nfe
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400);
GeneralPath path = new GeneralPath();
path.append(new Arc2D.Float(0, 0, 21600, adjval, 0, 180, Arc2D.OPEN), false);
path.moveTo(0, adjval/2);
path.lineTo(0, 21600 - adjval/2);
path.closePath();
path.append(new Arc2D.Float(0, 21600 - adjval, 21600, adjval, 180, 180, Arc2D.OPEN), false);
path.moveTo(21600, 21600 - adjval/2);
path.lineTo(21600, adjval/2);
path.append(new Arc2D.Float(0, 0, 21600, adjval, 180, 180, Arc2D.OPEN), false);
path.moveTo(0, adjval/2);
path.closePath();
return path;
}
};
shapes[ShapeTypes.LeftBrace] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
//m21600,qx10800@0l10800@2qy0@11,10800@3l10800@1qy21600,21600e
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 1800);
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 10800);
GeneralPath path = new GeneralPath();
path.moveTo(21600, 0);
path.append(new Arc2D.Float(10800, 0, 21600, adjval*2, 90, 90, Arc2D.OPEN), false);
path.moveTo(10800, adjval);
path.lineTo(10800, adjval2 - adjval);
path.append(new Arc2D.Float(-10800, adjval2 - 2*adjval, 21600, adjval*2, 270, 90, Arc2D.OPEN), false);
path.moveTo(0, adjval2);
path.append(new Arc2D.Float(-10800, adjval2, 21600, adjval*2, 0, 90, Arc2D.OPEN), false);
path.moveTo(10800, adjval2 + adjval);
path.lineTo(10800, 21600 - adjval);
path.append(new Arc2D.Float(10800, 21600 - 2*adjval, 21600, adjval*2, 180, 90, Arc2D.OPEN), false);
return path;
}
};
shapes[ShapeTypes.RightBrace] = new ShapeOutline(){
public java.awt.Shape getOutline(Shape shape){
//m,qx10800@0 l10800@2qy21600@11,10800@3l10800@1qy,21600e
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 1800);
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 10800);
GeneralPath path = new GeneralPath();
path.moveTo(0, 0);
path.append(new Arc2D.Float(-10800, 0, 21600, adjval*2, 0, 90, Arc2D.OPEN), false);
path.moveTo(10800, adjval);
path.lineTo(10800, adjval2 - adjval);
path.append(new Arc2D.Float(10800, adjval2 - 2*adjval, 21600, adjval*2, 180, 90, Arc2D.OPEN), false);
path.moveTo(21600, adjval2);
path.append(new Arc2D.Float(10800, adjval2, 21600, adjval*2, 90, 90, Arc2D.OPEN), false);
path.moveTo(10800, adjval2 + adjval);
path.lineTo(10800, 21600 - adjval);
path.append(new Arc2D.Float(-10800, 21600 - 2*adjval, 21600, adjval*2, 270, 90, Arc2D.OPEN), false);
return path;
}
};
}
}

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -19,6 +18,14 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.ddf.EscherContainerRecord; 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 * Background shape
@ -35,4 +42,34 @@ public class Background extends Shape {
return null; 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;
}
}
} }

View File

@ -22,12 +22,10 @@ import org.apache.poi.ddf.*;
import org.apache.poi.hslf.record.*; import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.PictureData;
import org.apache.poi.hslf.usermodel.SlideShow; 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.POILogger;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import java.awt.*; import java.awt.*;
import java.util.*;
/** /**
* Represents functionality provided by the 'Fill Effects' dialog in PowerPoint. * 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); EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
EscherSimpleProperty p1 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLCOLOR); EscherSimpleProperty p1 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLCOLOR);
EscherSimpleProperty p2 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); 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 p2val = p2 == null ? 0 : p2.getPropertyValue();
int alpha = p3 == null ? 255 : ((p3.getPropertyValue() >> 8) & 0xFF);
Color clr = null; Color clr = null;
if (p1 != null && (p2val & 0x10) != 0){ if (p1 != null && (p2val & 0x10) != 0){
int rgb = p1.getPropertyValue(); int rgb = p1.getPropertyValue();
clr = shape.getColor(rgb); clr = shape.getColor(rgb, alpha);
} }
return clr; return clr;
} }
@ -176,7 +176,7 @@ public class Fill {
Color clr = null; Color clr = null;
if (p1 != null && (p2val & 0x10) != 0){ if (p1 != null && (p2val & 0x10) != 0){
int rgb = p1.getPropertyValue(); int rgb = p1.getPropertyValue();
clr = shape.getColor(rgb); clr = shape.getColor(rgb, 255);
} }
return clr; return clr;
} }

View File

@ -22,6 +22,7 @@ import org.apache.poi.util.POILogger;
import java.awt.geom.*; import java.awt.geom.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
/** /**
* A "Freeform" shape. * A "Freeform" shape.
@ -33,6 +34,16 @@ import java.util.ArrayList;
* @author Yegor Kozlov * @author Yegor Kozlov
*/ */
public class Freeform extends AutoShape { public class Freeform extends AutoShape {
public static final byte[] SEGMENTINFO_MOVETO = new byte[]{0x00, 0x40};
public static final byte[] SEGMENTINFO_LINETO = new byte[]{0x00, (byte)0xAC};
public static final byte[] SEGMENTINFO_ESCAPE = new byte[]{0x01, 0x00};
public static final byte[] SEGMENTINFO_ESCAPE2 = new byte[]{0x01, 0x20};
public static final byte[] SEGMENTINFO_CUBICTO = new byte[]{0x00, (byte)0xAD};
public static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; //OpenOffice inserts 0xB3 instead of 0xAD.
public static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60};
public static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80};
/** /**
* Create a Freeform object and initialize it from the supplied Record container. * Create a Freeform object and initialize it from the supplied Record container.
* *
@ -82,36 +93,37 @@ public class Freeform extends AutoShape {
switch (type) { switch (type) {
case PathIterator.SEG_MOVETO: case PathIterator.SEG_MOVETO:
pntInfo.add(new Point2D.Double(vals[0], vals[1])); pntInfo.add(new Point2D.Double(vals[0], vals[1]));
segInfo.add(new byte[]{0x00, 0x40}); segInfo.add(SEGMENTINFO_MOVETO);
break; break;
case PathIterator.SEG_LINETO: case PathIterator.SEG_LINETO:
pntInfo.add(new Point2D.Double(vals[0], vals[1])); pntInfo.add(new Point2D.Double(vals[0], vals[1]));
segInfo.add(new byte[]{0x00, (byte)0xAC}); segInfo.add(SEGMENTINFO_LINETO);
segInfo.add(new byte[]{0x01, 0x00 }); segInfo.add(SEGMENTINFO_ESCAPE);
break; break;
case PathIterator.SEG_CUBICTO: case PathIterator.SEG_CUBICTO:
pntInfo.add(new Point2D.Double(vals[0], vals[1])); pntInfo.add(new Point2D.Double(vals[0], vals[1]));
pntInfo.add(new Point2D.Double(vals[2], vals[3])); pntInfo.add(new Point2D.Double(vals[2], vals[3]));
pntInfo.add(new Point2D.Double(vals[4], vals[5])); pntInfo.add(new Point2D.Double(vals[4], vals[5]));
segInfo.add(new byte[]{0x00, (byte)0xAD}); segInfo.add(SEGMENTINFO_CUBICTO);
segInfo.add(new byte[]{0x01, 0x20 }); segInfo.add(SEGMENTINFO_ESCAPE2);
break; break;
case PathIterator.SEG_QUADTO: case PathIterator.SEG_QUADTO:
//TODO: figure out how to convert SEG_QUADTO into SEG_CUBICTO
logger.log(POILogger.WARN, "SEG_QUADTO is not supported"); logger.log(POILogger.WARN, "SEG_QUADTO is not supported");
break; break;
case PathIterator.SEG_CLOSE: case PathIterator.SEG_CLOSE:
pntInfo.add(pntInfo.get(0)); pntInfo.add(pntInfo.get(0));
segInfo.add(new byte[]{0x00, (byte)0xAC}); segInfo.add(SEGMENTINFO_LINETO);
segInfo.add(new byte[]{0x01, 0x00 }); segInfo.add(SEGMENTINFO_ESCAPE);
segInfo.add(new byte[]{0x00, (byte)0xAC}); segInfo.add(SEGMENTINFO_LINETO);
segInfo.add(new byte[]{0x01, (byte)0x60}); segInfo.add(SEGMENTINFO_CLOSE);
isClosed = true; isClosed = true;
break; break;
} }
it.next(); it.next();
} }
if(!isClosed) segInfo.add(new byte[]{0x00, (byte)0xAC}); if(!isClosed) segInfo.add(SEGMENTINFO_LINETO);
segInfo.add(new byte[]{0x00, (byte)0x80}); segInfo.add(new byte[]{0x00, (byte)0x80});
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
@ -147,4 +159,85 @@ public class Freeform extends AutoShape {
setAnchor(bounds); setAnchor(bounds);
} }
/**
* Gets the freeform path
*
* @return the freeform path
*/
public GeneralPath getPath(){
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4));
EscherArrayProperty verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000));
if(verticesProp == null) verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES));
EscherArrayProperty segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000));
if(segmentsProp == null) segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO));
//sanity check
if(verticesProp == null) {
logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES ");
return null;
}
if(segmentsProp == null) {
logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO ");
return null;
}
Rectangle2D bounds = getAnchor2D();
float right = (float)bounds.getX();
float bottom = (float)bounds.getY();
GeneralPath path = new GeneralPath();
int numPoints = verticesProp.getNumberOfElementsInArray();
int numSegments = segmentsProp.getNumberOfElementsInArray();
for (int i = 0, j = 0; i < numSegments && j < numPoints; i++) {
byte[] elem = segmentsProp.getElement(i);
if(Arrays.equals(elem, SEGMENTINFO_MOVETO)){
byte[] p = verticesProp.getElement(j++);
short x = LittleEndian.getShort(p, 0);
short y = LittleEndian.getShort(p, 2);
path.moveTo(
((float)x*POINT_DPI/MASTER_DPI + right),
((float)y*POINT_DPI/MASTER_DPI + bottom));
} else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){
i++;
byte[] p1 = verticesProp.getElement(j++);
short x1 = LittleEndian.getShort(p1, 0);
short y1 = LittleEndian.getShort(p1, 2);
byte[] p2 = verticesProp.getElement(j++);
short x2 = LittleEndian.getShort(p2, 0);
short y2 = LittleEndian.getShort(p2, 2);
byte[] p3 = verticesProp.getElement(j++);
short x3 = LittleEndian.getShort(p3, 0);
short y3 = LittleEndian.getShort(p3, 2);
path.curveTo(
((float)x1*POINT_DPI/MASTER_DPI + right), ((float)y1*POINT_DPI/MASTER_DPI + bottom),
((float)x2*POINT_DPI/MASTER_DPI + right), ((float)y2*POINT_DPI/MASTER_DPI + bottom),
((float)x3*POINT_DPI/MASTER_DPI + right), ((float)y3*POINT_DPI/MASTER_DPI + bottom));
} else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){
i++;
byte[] pnext = segmentsProp.getElement(i);
if(Arrays.equals(pnext, SEGMENTINFO_ESCAPE)){
if(j + 1 < numPoints){
byte[] p = verticesProp.getElement(j++);
short x = LittleEndian.getShort(p, 0);
short y = LittleEndian.getShort(p, 2);
path.lineTo(
((float)x*POINT_DPI/MASTER_DPI + right), ((float)y*POINT_DPI/MASTER_DPI + bottom));
}
} else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){
path.closePath();
}
}
}
return path;
}
public java.awt.Shape getOutline(){
return getPath();
}
} }

View File

@ -19,6 +19,9 @@ package org.apache.poi.hslf.model;
import org.apache.poi.ddf.*; import org.apache.poi.ddf.*;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Line2D;
/** /**
* Represents a line in a PowerPoint drawing * Represents a line in a PowerPoint drawing
* *
@ -126,4 +129,8 @@ public class Line extends SimpleShape {
return _escherContainer; 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());
}
} }

View File

@ -17,6 +17,8 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.hslf.record.SheetContainer; 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; 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) ; 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;
}
} }

View File

@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.poi.hslf.model;
import org.apache.poi.ddf.*;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hslf.usermodel.ObjectData;
import org.apache.poi.hslf.record.ExObjList;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.ExEmbed;
import org.apache.poi.util.POILogger;
/**
* A shape representing embedded OLE obejct.
*
* @author Yegor Kozlov
*/
public class OLEShape extends Picture {
protected ExEmbed _exEmbed;
/**
* Create a new <code>OLEShape</code>
*
* @param idx the index of the picture
*/
public OLEShape(int idx){
super(idx);
}
/**
* Create a new <code>OLEShape</code>
*
* @param idx the index of the picture
* @param parent the parent shape
*/
public OLEShape(int idx, Shape parent) {
super(idx, parent);
}
/**
* Create a <code>OLEShape</code> object
*
* @param escherRecord the <code>EscherSpContainer</code> record which holds information about
* this picture in the <code>Slide</code>
* @param parent the parent shape of this picture
*/
protected OLEShape(EscherContainerRecord escherRecord, Shape parent){
super(escherRecord, parent);
}
/**
* Returns unique identifier for the OLE object.
*
* @return the unique identifier for the OLE object
*/
public int getObjectID(){
return getEscherProperty(EscherProperties.BLIP__PICTUREID);
}
/**
* Returns unique identifier for the OLE object.
*
* @return the unique identifier for the OLE object
*/
public ObjectData getObjectData(){
SlideShow ppt = getSheet().getSlideShow();
ObjectData[] ole = ppt.getEmbeddedObjects();
//persist reference
int ref = getExEmbed().getExOleObjAtom().getObjStgDataRef();
for (int i = 0; i < ole.length; i++) {
if(ole[i].getExOleObjStg().getPersistId() == ref) return ole[i];
}
logger.log(POILogger.WARN, "OLE data not found");
return null;
}
/**
* Return the record container for this embedded object.
*
* <p>
* It contains:
* 1. ExEmbedAtom.(4045)
* 2. ExOleObjAtom (4035)
* 3. CString (4026), Instance MenuName (1) used for menus and the Links dialog box.
* 4. CString (4026), Instance ProgID (2) that stores the OLE Programmatic Identifier.
* A ProgID is a string that uniquely identifies a given object.
* 5. CString (4026), Instance ClipboardName (3) that appears in the paste special dialog.
* 6. MetaFile( 4033), optional
* </p>
* @return
*/
public ExEmbed getExEmbed(){
if(_exEmbed == null){
SlideShow ppt = getSheet().getSlideShow();
ExObjList lst = ppt.getDocumentRecord().getExObjList();
if(lst == null){
logger.log(POILogger.WARN, "ExObjList not found");
return null;
}
int id = getObjectID();
Record[] ch = lst.getChildRecords();
for (int i = 0; i < ch.length; i++) {
if(ch[i] instanceof ExEmbed){
ExEmbed embd = (ExEmbed)ch[i];
if( embd.getExOleObjAtom().getObjID() == id) _exEmbed = embd;
}
}
}
return _exEmbed;
}
/**
* Returns the instance name of the embedded object, e.g. "Document" or "Workbook".
*
* @return the instance name of the embedded object
*/
public String getInstanceName(){
return getExEmbed().getMenuName();
}
/**
* Returns the full name of the embedded object,
* e.g. "Microsoft Word Document" or "Microsoft Office Excel Worksheet".
*
* @return the full name of the embedded object
*/
public String getFullName(){
return getExEmbed().getClipboardName();
}
/**
* Returns the ProgID that stores the OLE Programmatic Identifier.
* A ProgID is a string that uniquely identifies a given object, for example,
* "Word.Document.8" or "Excel.Sheet.8".
*
* @return the ProgID
*/
public String getProgID(){
return getExEmbed().getProgId();
}
}

View File

@ -27,7 +27,6 @@ import java.awt.image.renderable.RenderableImage;
import java.awt.geom.*; import java.awt.geom.*;
import java.text.AttributedCharacterIterator; import java.text.AttributedCharacterIterator;
import java.util.Map; import java.util.Map;
import java.util.ArrayList;
import org.apache.poi.hslf.usermodel.RichTextRun; import org.apache.poi.hslf.usermodel.RichTextRun;
import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;

View File

@ -21,13 +21,16 @@ import org.apache.poi.hslf.usermodel.PictureData;
import org.apache.poi.hslf.usermodel.SlideShow; import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hslf.record.Document; import org.apache.poi.hslf.record.Document;
import org.apache.poi.hslf.blip.Bitmap; import org.apache.poi.hslf.blip.Bitmap;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.*; import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List; import java.util.List;
import java.util.Arrays; import java.util.Arrays;
@ -128,7 +131,7 @@ public class Picture extends SimpleShape {
//set default properties for a picture //set default properties for a picture
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); 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. //another weird feature of powerpoint: for picture id we must add 0x4000.
setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx); setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx);
@ -192,14 +195,70 @@ public class Picture extends SimpleShape {
return null; 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 * By default set the orininal image size
*/ */
protected void afterInsert(Sheet sh){ protected void afterInsert(Sheet sh){
super.afterInsert(sh);
java.awt.Rectangle anchor = getAnchor(); java.awt.Rectangle anchor = getAnchor();
if (anchor.equals(new java.awt.Rectangle())){ if (anchor.equals(new java.awt.Rectangle())){
setDefaultSize(); 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()));
}
}
} }

View File

@ -17,12 +17,12 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.ddf.*; 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.ColorSchemeAtom;
import org.apache.poi.hslf.record.PPDrawing;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import java.util.Iterator; import java.util.*;
import java.awt.*; import java.awt.*;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
@ -166,6 +166,17 @@ public abstract class Shape {
if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ if ((flags & EscherSpRecord.FLAG_CHILD) != 0){
EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID);
anchor = new java.awt.Rectangle(); anchor = new java.awt.Rectangle();
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( anchor = new Rectangle2D.Float(
(float)rec.getDx1()*POINT_DPI/MASTER_DPI, (float)rec.getDx1()*POINT_DPI/MASTER_DPI,
(float)rec.getDy1()*POINT_DPI/MASTER_DPI, (float)rec.getDy1()*POINT_DPI/MASTER_DPI,
@ -173,6 +184,7 @@ public abstract class Shape {
(float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI (float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI
); );
} }
}
else { else {
EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID);
anchor = new java.awt.Rectangle(); anchor = new java.awt.Rectangle();
@ -186,6 +198,10 @@ public abstract class Shape {
return anchor; return anchor;
} }
public Rectangle2D getLogicalAnchor2D(){
return getAnchor2D();
}
/** /**
* Sets the anchor (the bounding box rectangle) of this shape. * Sets the anchor (the bounding box rectangle) of this shape.
* All coordinates should be expressed in points (72 dpi). * 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. * @return escher property or <code>null</code> if not found.
*/ */
public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){ 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(); EscherProperty prop = (EscherProperty) iterator.next();
if (prop.getPropertyNumber() == propId) if (prop.getPropertyNumber() == propId)
@ -294,7 +310,18 @@ public abstract class Shape {
public int getEscherProperty(short propId){ public int getEscherProperty(short propId){
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId); 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 * @param sh - owning shape
*/ */
protected void afterInsert(Sheet sh){ 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; _sheet = sheet;
} }
protected Color getColor(int rgb){ protected Color getColor(int rgb, int alpha){
if (rgb >= 0x8000000) { if (rgb >= 0x8000000) {
int idx = rgb - 0x8000000; int idx = rgb - 0x8000000;
ColorSchemeAtom ca = getSheet().getColorScheme(); ColorSchemeAtom ca = getSheet().getColorScheme();
if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx); if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx);
} }
Color tmp = new Color(rgb, true); 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); 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();
}
} }

View File

@ -76,13 +76,19 @@ public class ShapeFactory {
case ShapeTypes.TextBox: case ShapeTypes.TextBox:
shape = new TextBox(spContainer, parent); shape = new TextBox(spContainer, parent);
break; break;
case ShapeTypes.PictureFrame: 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); shape = new Picture(spContainer, parent);
break; break;
}
case ShapeTypes.Line: case ShapeTypes.Line:
shape = new Line(spContainer, parent); shape = new Line(spContainer, parent);
break; break;
case ShapeTypes.NotPrimitive: case ShapeTypes.NotPrimitive: {
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID); EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES); EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES);
if(prop != null) if(prop != null)
@ -93,6 +99,7 @@ public class ShapeFactory {
shape = new AutoShape(spContainer, parent); shape = new AutoShape(spContainer, parent);
} }
break; break;
}
default: default:
shape = new AutoShape(spContainer, parent); shape = new AutoShape(spContainer, parent);
break; break;

View File

@ -24,6 +24,8 @@ import org.apache.poi.hslf.record.EscherTextboxWrapper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
import java.awt.*;
/** /**
* Represents a group of shapes. * Represents a group of shapes.
@ -113,6 +115,47 @@ public class ShapeGroup extends Shape{
spgr.setRectY2((anchor.y + anchor.height)*MASTER_DPI/POINT_DPI); 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 * 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 * @return the anchor of this shape group
*/ */
public Rectangle2D getAnchor2D(){ public Rectangle2D getAnchor2D(){
EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0); EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0);
EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(groupInfoContainer, EscherSpgrRecord.RECORD_ID); EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID);
Rectangle2D anchor = new Rectangle2D.Float( Rectangle2D.Float anchor = new Rectangle2D.Float();
(float)spgr.getRectX1()*POINT_DPI/MASTER_DPI, if(clientAnchor == null){
(float)spgr.getRectY1()*POINT_DPI/MASTER_DPI, logger.log(POILogger.INFO, "EscherClientAnchorRecord was not found for shape group. Searching for EscherChildAnchorRecord.");
(float)(spgr.getRectX2() - spgr.getRectX1())*POINT_DPI/MASTER_DPI, EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(spContainer, EscherChildAnchorRecord.RECORD_ID);
(float)(spgr.getRectY2() - spgr.getRectY1())*POINT_DPI/MASTER_DPI 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; return anchor;
} }
@ -223,4 +276,26 @@ public class ShapeGroup extends Shape{
return null; 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);
}
} }

View File

@ -0,0 +1,27 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.model;
/**
* Date: Apr 17, 2008
*
* @author Yegor Kozlov
*/
public interface ShapeOutline {
java.awt.Shape getOutline(Shape shape);
}

View File

@ -0,0 +1,102 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.model;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory;
import java.awt.*;
import java.awt.geom.Rectangle2D;
/**
* Paint a shape into java.awt.Graphics2D
*
* @author Yegor Kozlov
*/
public class ShapePainter {
protected static POILogger logger = POILogFactory.getLogger(ShapePainter.class);
public static void paint(SimpleShape shape, Graphics2D graphics){
Rectangle2D anchor = shape.getLogicalAnchor2D();
java.awt.Shape outline = shape.getOutline();
//flip vertical
if(shape.getFlipVertical()){
graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
graphics.scale(1, -1);
graphics.translate(-anchor.getX(), -anchor.getY());
}
//flip horizontal
if(shape.getFlipHorizontal()){
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
graphics.scale(-1, 1);
graphics.translate(-anchor.getX() , -anchor.getY());
}
//rotate transform
double angle = shape.getRotation();
if(angle != 0){
double centerX = anchor.getX() + anchor.getWidth()/2;
double centerY = anchor.getY() + anchor.getHeight()/2;
graphics.translate(centerX, centerY);
graphics.rotate(Math.toRadians(angle));
graphics.translate(-centerX, -centerY);
}
//fill
Color fillColor = shape.getFill().getForegroundColor();
if (fillColor != null) {
//TODO: implement gradient and texture fill patterns
graphics.setPaint(fillColor);
graphics.fill(outline);
}
//border
Color lineColor = shape.getLineColor();
if (lineColor != null){
graphics.setPaint(lineColor);
float width = (float)shape.getLineWidth();
if(width == 0) width = 0.75f;
int dashing = shape.getLineDashing();
//TODO: implement more dashing styles
float[] dashptrn = null;
switch(dashing){
case Line.PEN_SOLID:
dashptrn = null;
break;
case Line.PEN_PS_DASH:
dashptrn = new float[]{width, width};
break;
case Line.PEN_DOTGEL:
dashptrn = new float[]{width*4, width*3};
break;
default:
logger.log(POILogger.WARN, "unsupported dashing: " + dashing);
dashptrn = new float[]{width, width};
break;
}
Stroke stroke = new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dashptrn, 0.0f);
graphics.setStroke(stroke);
graphics.draw(outline);
}
}
}

View File

@ -29,6 +29,7 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
import java.awt.*;
/** /**
* This class defines the common format of "Sheets" in a powerpoint * 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); EscherContainerRecord spgr = (EscherContainerRecord) Shape.getEscherChild(dgContainer, EscherContainerRecord.SPGR_CONTAINER);
spgr.addChildRecord(shape.getSpContainer()); 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.setSheet(this);
shape.afterInsert(this); shape.afterInsert(this);
@ -329,4 +322,7 @@ public abstract class Sheet {
return _background; return _background;
} }
public void draw(Graphics2D graphics){
}
} }

View File

@ -22,6 +22,8 @@ import org.apache.poi.util.LittleEndian;
import org.apache.poi.hslf.record.ColorSchemeAtom; import org.apache.poi.hslf.record.ColorSchemeAtom;
import java.awt.*; import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/** /**
* An abstract simple (non-group) shape. * An abstract simple (non-group) shape.
@ -199,4 +201,87 @@ public class SimpleShape extends Shape {
getFill().setForegroundColor(color); 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);
}
} }

View File

@ -21,17 +21,12 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import java.util.Vector; import java.util.Vector;
import java.util.List; import java.awt.*;
import java.util.Iterator;
import java.util.ArrayList;
import org.apache.poi.hslf.record.PPDrawing;
import org.apache.poi.hslf.record.SlideAtom; import org.apache.poi.hslf.record.SlideAtom;
import org.apache.poi.hslf.record.TextHeaderAtom; import org.apache.poi.hslf.record.TextHeaderAtom;
import org.apache.poi.hslf.record.ColorSchemeAtom; import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; 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 * This class represents a slide in a PowerPoint Document. It allows
@ -263,10 +258,85 @@ public class Slide extends Sheet
return sa.getFollowMasterBackground(); return sa.getFollowMasterBackground();
} }
/**
* 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() { public Background getBackground() {
if(getFollowMasterBackground()) if(getFollowMasterBackground())
return getMasterSheet().getBackground(); return getMasterSheet().getBackground();
else else
return super.getBackground(); 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);
}
}
} }

View 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.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.record.*; import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.usermodel.SlideShow; 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. * SlideMaster determines the graphics, layout, and formatting for all the slides in a given presentation.
@ -82,6 +76,7 @@ public class SlideMaster extends MasterSheet {
if (prop != null) break; if (prop != null) break;
} }
if (prop == null) { if (prop == null) {
if(isCharacter) {
switch (txtype) { switch (txtype) {
case TextHeaderAtom.CENTRE_BODY_TYPE: case TextHeaderAtom.CENTRE_BODY_TYPE:
case TextHeaderAtom.HALF_BODY_TYPE: case TextHeaderAtom.HALF_BODY_TYPE:
@ -94,6 +89,19 @@ public class SlideMaster extends MasterSheet {
default: default:
return null; 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); prop = getStyleAttribute(txtype, level, name, isCharacter);
} }
return prop; return prop;
@ -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;
}
} }

View File

@ -23,7 +23,6 @@ import org.apache.poi.util.LittleEndian;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import java.awt.*; import java.awt.*;
import java.awt.geom.Rectangle2D;
/** /**
* Represents a table in a PowerPoint presentation * Represents a table in a PowerPoint presentation
@ -113,6 +112,8 @@ public class Table extends ShapeGroup {
} }
protected void afterInsert(Sheet sh){ protected void afterInsert(Sheet sh){
super.afterInsert(sh);
EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0); EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0);
List lst = spCont.getChildRecords(); List lst = spCont.getChildRecords();
EscherOptRecord opt = (EscherOptRecord)lst.get(lst.size()-2); EscherOptRecord opt = (EscherOptRecord)lst.get(lst.size()-2);

View File

@ -18,9 +18,7 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.ddf.*; import org.apache.poi.ddf.*;
import org.apache.poi.hslf.record.EscherTextboxWrapper;
import org.apache.poi.hslf.record.TextHeaderAtom; import org.apache.poi.hslf.record.TextHeaderAtom;
import org.apache.poi.hslf.usermodel.RichTextRun;
import java.awt.*; import java.awt.*;

View File

@ -19,15 +19,6 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.ddf.*; 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. * Represents a TextFrame shape in PowerPoint.

View File

@ -0,0 +1,253 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.model;
import org.apache.poi.hslf.usermodel.RichTextRun;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory;
import java.text.AttributedString;
import java.text.AttributedCharacterIterator;
import java.awt.font.TextAttribute;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextLayout;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
/**
* Paint text into java.awt.Graphics2D
*
* @author Yegor Kozlov
*/
public class TextPainter {
protected POILogger logger = POILogFactory.getLogger(this.getClass());
protected TextShape _shape;
public TextPainter(TextShape shape){
_shape = shape;
}
/**
* Convert the underlying set of rich text runs into java.text.AttributedString
*/
public AttributedString getAttributedString(TextRun txrun){
String text = txrun.getText();
AttributedString at = new AttributedString(text);
RichTextRun[] rt = txrun.getRichTextRuns();
for (int i = 0; i < rt.length; i++) {
int start = rt[i].getStartIndex();
int end = rt[i].getEndIndex();
if(start == end) {
logger.log(POILogger.INFO, "Skipping RichTextRun with zero length");
continue;
}
at.addAttribute(TextAttribute.FAMILY, rt[i].getFontName(), start, end);
at.addAttribute(TextAttribute.SIZE, new Float(rt[i].getFontSize()), start, end);
at.addAttribute(TextAttribute.FOREGROUND, rt[i].getFontColor(), start, end);
if(rt[i].isBold()) at.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, start, end);
if(rt[i].isItalic()) at.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, start, end);
if(rt[i].isUnderlined()) {
at.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, start, end);
at.addAttribute(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, start, end);
}
if(rt[i].isStrikethrough()) at.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, start, end);
int superScript = rt[i].getSuperscript();
if(superScript != 0) at.addAttribute(TextAttribute.SUPERSCRIPT, superScript > 0 ? TextAttribute.SUPERSCRIPT_SUPER : TextAttribute.SUPERSCRIPT_SUB, start, end);
}
return at;
}
public void paint(Graphics2D graphics){
TextRun run = _shape.getTextRun();
if (run == null) return;
String text = run.getText();
if (text == null || text.equals("")) return;
AttributedString at = getAttributedString(run);
AttributedCharacterIterator it = at.getIterator();
int paragraphStart = it.getBeginIndex();
int paragraphEnd = it.getEndIndex();
Rectangle2D anchor = _shape.getLogicalAnchor2D();
float textHeight = 0;
ArrayList lines = new ArrayList();
LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());
measurer.setPosition(paragraphStart);
while (measurer.getPosition() < paragraphEnd) {
int startIndex = measurer.getPosition();
int nextBreak = text.indexOf('\n', measurer.getPosition() + 1);
boolean prStart = text.charAt(startIndex) == '\n';
if(prStart) measurer.setPosition(startIndex++);
RichTextRun rt = run.getRichTextRunAt(startIndex == text.length() ? (startIndex-1) : startIndex);
if(rt == null) {
logger.log(POILogger.WARN, "RichTextRun not found at pos" + startIndex + "; text.length: " + text.length());
break;
}
float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight();
wrappingWidth -= rt.getTextOffset();
if (_shape.getWordWrap() == TextShape.WrapNone) {
wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width;
}
TextLayout textLayout = measurer.nextLayout(wrappingWidth + 1,
nextBreak == -1 ? paragraphEnd : nextBreak, true);
if (textLayout == null) {
textLayout = measurer.nextLayout((float)anchor.getWidth(),
nextBreak == -1 ? paragraphEnd : nextBreak, false);
}
if(textLayout == null){
logger.log(POILogger.WARN, "Failed to break text into lines: wrappingWidth: "+wrappingWidth+
"; text: " + rt.getText());
measurer.setPosition(rt.getEndIndex());
continue;
}
int endIndex = measurer.getPosition();
float lineHeight = (float)textLayout.getBounds().getHeight();
int linespacing = rt.getLineSpacing();
if(linespacing == 0) linespacing = 100;
TextElement el = new TextElement();
if(linespacing >= 0){
el.ascent = textLayout.getAscent()*linespacing/100;
} else {
el.ascent = -linespacing*Shape.POINT_DPI/Shape.MASTER_DPI;
}
el._align = rt.getAlignment();
el._text = textLayout;
el._textOffset = rt.getTextOffset();
if (prStart){
int sp = rt.getSpaceBefore();
float spaceBefore;
if(sp >= 0){
spaceBefore = lineHeight * sp/100;
} else {
spaceBefore = -sp*Shape.POINT_DPI/Shape.MASTER_DPI;
}
el.ascent += spaceBefore;
}
float descent;
if(linespacing >= 0){
descent = (textLayout.getDescent() + textLayout.getLeading())*linespacing/100;
} else {
descent = -linespacing*Shape.POINT_DPI/Shape.MASTER_DPI;
}
if (prStart){
int sp = rt.getSpaceAfter();
float spaceAfter;
if(sp >= 0){
spaceAfter = lineHeight * sp/100;
} else {
spaceAfter = -sp*Shape.POINT_DPI/Shape.MASTER_DPI;
}
el.ascent += spaceAfter;
}
el.descent = descent;
textHeight += el.ascent + el.descent;
if(rt.isBullet() && (prStart || startIndex == 0)){
it.setIndex(startIndex);
AttributedString bat = new AttributedString(Character.toString(rt.getBulletChar()));
Color clr = rt.getBulletColor();
if (clr != null) bat.addAttribute(TextAttribute.FOREGROUND, clr);
else bat.addAttribute(TextAttribute.FOREGROUND, it.getAttribute(TextAttribute.FOREGROUND));
bat.addAttribute(TextAttribute.FAMILY, it.getAttribute(TextAttribute.FAMILY));
bat.addAttribute(TextAttribute.SIZE, it.getAttribute(TextAttribute.SIZE));
TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext());
if(text.substring(startIndex, endIndex).length() > 1){
el._bullet = bulletLayout;
el._bulletOffset = rt.getBulletOffset();
}
}
lines.add(el);
}
int valign = _shape.getVerticalAlignment();
double y0 = anchor.getY();
switch (valign){
case TextShape.AnchorTopBaseline:
case TextShape.AnchorTop:
y0 += _shape.getMarginTop();
break;
case TextShape.AnchorBottom:
y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();
break;
default:
case TextShape.AnchorMiddle:
float delta = (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();
y0 += _shape.getMarginTop() + delta/2;
break;
}
//finally draw the text fragments
for (int i = 0; i < lines.size(); i++) {
TextElement elem = (TextElement)lines.get(i);
y0 += elem.ascent;
Point2D.Double pen = new Point2D.Double();
pen.y = y0;
switch (elem._align) {
default:
case TextShape.AlignLeft:
pen.x = anchor.getX() + _shape.getMarginLeft();
break;
case TextShape.AlignCenter:
pen.x = anchor.getX() + _shape.getMarginLeft() +
(anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;
break;
case TextShape.AlignRight:
pen.x = anchor.getX() + _shape.getMarginLeft() +
(anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight());
break;
}
if(elem._bullet != null){
elem._bullet.draw(graphics, (float)(pen.x + elem._bulletOffset), (float)pen.y);
}
elem._text.draw(graphics, (float)(pen.x + elem._textOffset), (float)pen.y);
y0 += elem.descent;
}
}
static class TextElement {
public TextLayout _text;
public int _textOffset;
public TextLayout _bullet;
public int _bulletOffset;
public int _align;
public float ascent, descent;
}
}

View File

@ -534,6 +534,10 @@ public class TextRun
// The messes things up on everything but a Mac, so translate // The messes things up on everything but a Mac, so translate
// them to \n // them to \n
String text = rawText.replace('\r','\n'); String text = rawText.replace('\r','\n');
//0xB acts like cariage return in page titles
text = text.replace((char) 0x0B, '\n');
return text; return text;
} }
@ -635,4 +639,20 @@ public class TextRun
public Hyperlink[] getHyperlinks(){ public Hyperlink[] getHyperlinks(){
return Hyperlink.find(this); 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;
}
} }

View File

@ -184,6 +184,8 @@ public abstract class TextShape extends SimpleShape {
* @param sh the sheet we are adding to * @param sh the sheet we are adding to
*/ */
protected void afterInsert(Sheet sh){ protected void afterInsert(Sheet sh){
super.afterInsert(sh);
EscherTextboxWrapper _txtbox = getEscherTextboxWrapper(); EscherTextboxWrapper _txtbox = getEscherTextboxWrapper();
if(_txtbox != null){ if(_txtbox != null){
PPDrawing ppdrawing = sh.getPPDrawing(); PPDrawing ppdrawing = sh.getPPDrawing();
@ -513,4 +515,12 @@ public abstract class TextShape extends SimpleShape {
} }
} }
} }
public void draw(Graphics2D graphics){
AffineTransform at = graphics.getTransform();
ShapePainter.paint(this, graphics);
new TextPainter(this).paint(graphics);
graphics.setTransform(at);
}
} }

View File

@ -30,7 +30,10 @@ import org.apache.poi.util.LittleEndian;
* *
* @author Daniel Noll * @author Daniel Noll
*/ */
public class ExOleObjStg extends RecordAtom { public class ExOleObjStg extends RecordAtom implements PersistRecord {
private int _persistId; // Found from PersistPtrHolder
/** /**
* Record header. * Record header.
*/ */
@ -109,4 +112,19 @@ public class ExOleObjStg extends RecordAtom {
out.write(_header); out.write(_header);
out.write(_data); 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;
}
} }

View File

@ -37,6 +37,8 @@ public class PPDrawingGroup extends RecordAtom {
private byte[] _header; private byte[] _header;
private EscherContainerRecord dggContainer; private EscherContainerRecord dggContainer;
//cached dgg
private EscherDggRecord dgg;
protected PPDrawingGroup(byte[] source, int start, int len) { protected PPDrawingGroup(byte[] source, int start, int len) {
// Get the header // Get the header
@ -116,4 +118,17 @@ public class PPDrawingGroup extends RecordAtom {
public EscherContainerRecord getDggContainer(){ public EscherContainerRecord getDggContainer(){
return dggContainer; 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;
}
} }

View File

@ -0,0 +1,37 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
/**
* A record that can be referenced in PersistPtr storage.
*
* @author Yegor Kozlov
*/
public interface PersistRecord {
/**
* Fetch the persist ID
*/
public int getPersistId();
/**
* Set the persist ID
*/
public void setPersistId(int id);
}

View File

@ -70,9 +70,10 @@ public class RecordTypes {
public static final Type List = new Type(2000,null); public static final Type List = new Type(2000,null);
public static final Type FontCollection = new Type(2005,FontCollection.class); public static final Type FontCollection = new Type(2005,FontCollection.class);
public static final Type BookmarkCollection = new Type(2019,null); 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 SoundCollAtom = new Type(2021,null);
public static final Type Sound = new Type(2022,null); public static final Type Sound = new Type(2022,Sound.class);
public static final Type SoundData = new Type(2023,null); public static final Type SoundData = new Type(2023,SoundData.class);
public static final Type BookmarkSeedAtom = new Type(2025,null); public static final Type BookmarkSeedAtom = new Type(2025,null);
public static final Type ColorSchemeAtom = new Type(2032,ColorSchemeAtom.class); public static final Type ColorSchemeAtom = new Type(2032,ColorSchemeAtom.class);
public static final Type ExObjRefAtom = new Type(3009,null); public static final Type ExObjRefAtom = new Type(3009,null);

View File

@ -0,0 +1,136 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
import org.apache.poi.util.POILogger;
import java.io.OutputStream;
import java.io.IOException;
/**
* A container holding information about a sound. It contains:
* <p>
* <li>1. CString (4026), Instance 0: Name of sound (e.g. "crash")
* <li>2. CString (4026), Instance 1: Type of sound (e.g. ".wav")
* <li>3. CString (4026), Instance 2: Reference id of sound in sound collection
* <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.
* <li>5. SoundData (2023), optional
* </p>
*
* @author Yegor Kozlov
*/
public class Sound extends RecordContainer {
/**
* Record header data.
*/
private byte[] _header;
// Links to our more interesting children
private CString _name;
private CString _type;
private SoundData _data;
/**
* Set things up, and find our more interesting children
*
* @param source the source data as a byte array.
* @param start the start offset into the byte array.
* @param len the length of the slice in the byte array.
*/
protected Sound(byte[] source, int start, int len) {
// Grab the header
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
private void findInterestingChildren() {
// First child should be the ExHyperlinkAtom
if(_children[0] instanceof CString) {
_name = (CString)_children[0];
} else {
logger.log(POILogger.ERROR, "First child record wasn't a CString, was of type " + _children[0].getRecordType());
}
// Second child should be the ExOleObjAtom
if (_children[1] instanceof CString) {
_type = (CString)_children[1];
} else {
logger.log(POILogger.ERROR, "Second child record wasn't a CString, was of type " + _children[1].getRecordType());
}
for (int i = 2; i < _children.length; i++) {
if(_children[i] instanceof SoundData){
_data = (SoundData)_children[i];
break;
}
}
}
/**
* Returns the type (held as a little endian in bytes 3 and 4)
* that this class handles.
*
* @return the record type.
*/
public long getRecordType() {
return RecordTypes.Sound.typeID;
}
/**
* Have the contents printer out into an OutputStream, used when
* writing a file back out to disk.
*
* @param out the output stream.
* @throws java.io.IOException if there was an error writing to the stream.
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
/**
* Name of the sound (e.g. "crash")
*
* @return name of the sound
*/
public String getSoundName(){
return _name.getText();
}
/**
* Type of the sound (e.g. ".wav")
*
* @return type of the sound
*/
public String getSoundType(){
return _type.getText();
}
/**
* The sound data
*
* @return the sound data.
*/
public byte[] getSoundData(){
return _data == null ? null : _data.getData();
}
}

View File

@ -0,0 +1,73 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
import org.apache.poi.util.POILogger;
import java.io.OutputStream;
import java.io.IOException;
/**
* Is a container for all sound related atoms and containers. It contains:
*<li>1. SoundCollAtom (2021)
*<li>2. Sound (2022), for each sound, if any
*
* @author Yegor Kozlov
*/
public class SoundCollection extends RecordContainer {
/**
* Record header data.
*/
private byte[] _header;
/**
* Set things up, and find our more interesting children
*
* @param source the source data as a byte array.
* @param start the start offset into the byte array.
* @param len the length of the slice in the byte array.
*/
protected SoundCollection(byte[] source, int start, int len) {
// Grab the header
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
}
/**
* Returns the type (held as a little endian in bytes 3 and 4)
* that this class handles.
*
* @return the record type.
*/
public long getRecordType() {
return RecordTypes.SoundCollection.typeID;
}
/**
* Have the contents printer out into an OutputStream, used when
* writing a file back out to disk.
*
* @param out the output stream.
* @throws java.io.IOException if there was an error writing to the stream.
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
}

View File

@ -0,0 +1,103 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.InflaterInputStream;
import org.apache.poi.util.LittleEndian;
/**
* Storage for embedded sounds.
*
* @author Yegor Kozlov
*/
public class SoundData extends RecordAtom {
/**
* Record header.
*/
private byte[] _header;
/**
* Record data.
*/
private byte[] _data;
/**
* Constructs a new empty sound container.
*/
protected SoundData() {
_header = new byte[8];
_data = new byte[0];
LittleEndian.putShort(_header, 2, (short)getRecordType());
LittleEndian.putInt(_header, 4, _data.length);
}
/**
* Constructs the link related atom record from its
* source data.
*
* @param source the source data as a byte array.
* @param start the start offset into the byte array.
* @param len the length of the slice in the byte array.
*/
protected SoundData(byte[] source, int start, int len) {
// Get the header.
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
// Get the record data.
_data = new byte[len-8];
System.arraycopy(source,start+8,_data,0,len-8);
}
/**
* Returns the sound data.
*
* @return the sound data
*/
public byte[] getData() {
return _data;
}
/**
* Gets the record type.
*
* @return the record type.
*/
public long getRecordType() {
return RecordTypes.SoundData.typeID;
}
/**
* Write the contents of the record back, so it can be written
* to disk.
*
* @param out the output stream to write to.
* @throws java.io.IOException if an error occurs.
*/
public void writeOut(OutputStream out) throws IOException {
out.write(_header);
out.write(_data);
}
}

View File

@ -150,7 +150,7 @@ public class StyleTextPropAtom extends RecordAtom
new TextProp(2, 0x800000, "symbol"), new TextProp(2, 0x800000, "symbol"),
new TextProp(2, 0x20000, "font.size"), new TextProp(2, 0x20000, "font.size"),
new TextProp(4, 0x40000, "font.color"), 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, 0x100000, "char_unknown_1"),
new TextProp(2, 0x1000000, "char_unknown_3"), new TextProp(2, 0x1000000, "char_unknown_3"),
new TextProp(2, 0x2000000, "char_unknown_4"), new TextProp(2, 0x2000000, "char_unknown_4"),

View File

@ -48,4 +48,13 @@ public class ObjectData {
public InputStream getData() { public InputStream getData() {
return storage.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;
}
} }

View File

@ -37,9 +37,6 @@ import org.apache.poi.hslf.record.ColorSchemeAtom;
/** /**
* Represents a run of text, all with the same style * 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 { public class RichTextRun {
/** The TextRun we belong to */ /** The TextRun we belong to */
@ -126,6 +123,24 @@ public class RichTextRun {
return length; 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 * Fetch the text, in output suitable form
*/ */
@ -314,34 +329,142 @@ public class RichTextRun {
// --------------- Friendly getters / setters on rich text properties ------- // --------------- Friendly getters / setters on rich text properties -------
/**
* Is the text bold?
*/
public boolean isBold() { public boolean isBold() {
return isCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX); return isCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX);
} }
/**
* Is the text bold?
*/
public void setBold(boolean bold) { public void setBold(boolean bold) {
setCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX, bold); setCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX, bold);
} }
/**
* Is the text italic?
*/
public boolean isItalic() { public boolean isItalic() {
return isCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX); return isCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX);
} }
/**
* Is the text italic?
*/
public void setItalic(boolean italic) { public void setItalic(boolean italic) {
setCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX, italic); setCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX, italic);
} }
/**
* Is the text underlined?
*/
public boolean isUnderlined() { public boolean isUnderlined() {
return isCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX); return isCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX);
} }
/**
* Is the text underlined?
*/
public void setUnderlined(boolean underlined) { public void setUnderlined(boolean underlined) {
setCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX, 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() { public int getFontSize() {
return getCharTextPropVal("font.size"); return getCharTextPropVal("font.size");
} }
/**
* Sets the font size
*/
public void setFontSize(int fontSize) { public void setFontSize(int fontSize) {
setCharTextPropVal("font.size", 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) { public void setFontName(String fontName) {
if (slideShow == null) { if (slideShow == null) {
//we can't set font since slideshow is not assigned yet //we can't set font since slideshow is not assigned yet
@ -352,6 +475,10 @@ public class RichTextRun {
setCharTextPropVal("font.index", fontIdx); setCharTextPropVal("font.index", fontIdx);
} }
} }
/**
* Gets the font name
*/
public String getFontName() { public String getFontName() {
if (slideShow == null) { if (slideShow == null) {
return _fontname; return _fontname;
@ -368,12 +495,12 @@ public class RichTextRun {
*/ */
public Color getFontColor() { public Color getFontColor() {
int rgb = getCharTextPropVal("font.color"); int rgb = getCharTextPropVal("font.color");
if (rgb >= 0x8000000) {
int idx = rgb % 0x8000000;
ColorSchemeAtom ca = parentRun.getSheet().getColorScheme();
if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx);
}
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); Color tmp = new Color(rgb, true);
return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
} }
@ -427,7 +554,7 @@ public class RichTextRun {
/** /**
* Sets indentation level * 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) { public void setIndentLevel(int level) {
if(paragraphStyle != null ) paragraphStyle.setReservedField((short)level); if(paragraphStyle != null ) paragraphStyle.setReservedField((short)level);
@ -488,6 +615,133 @@ public class RichTextRun {
public int getTextOffset() { public int getTextOffset() {
return getParaTextPropVal("text.offset")*Shape.POINT_DPI/Shape.MASTER_DPI; 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! ------- // --------------- Internal HSLF methods, not intended for end-user use! -------
/** /**

View File

@ -488,6 +488,20 @@ public class SlideShow
return _hslfSlideShow.getPictures(); 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 * Return the current page size
*/ */

View File

@ -0,0 +1,93 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.usermodel;
import org.apache.poi.hslf.record.*;
import java.util.ArrayList;
/**
* A class that represents sound data embedded in a slide show.
*
* @author Yegor Kozlov
*/
public class SoundData {
/**
* The record that contains the object data.
*/
private Sound _container;
/**
* Creates the object data wrapping the record that contains the sound data.
*
* @param container the record that contains the sound data.
*/
public SoundData(Sound container) {
this._container = container;
}
/**
* Name of the sound (e.g. "crash")
*
* @return name of the sound
*/
public String getSoundName(){
return _container.getSoundName();
}
/**
* Type of the sound (e.g. ".wav")
*
* @return type of the sound
*/
public String getSoundType(){
return _container.getSoundType();
}
/**
* Gets an input stream which returns the binary of the sound data.
*
* @return the input stream which will contain the binary of the sound data.
*/
public byte[] getData() {
return _container.getSoundData();
}
/**
* Find all sound records in the supplied Document records
*
* @param document the document to find in
* @return the array with the sound data
*/
public static SoundData[] find(Document document){
ArrayList lst = new ArrayList();
Record[] ch = document.getChildRecords();
for (int i = 0; i < ch.length; i++) {
if(ch[i].getRecordType() == RecordTypes.SoundCollection.typeID){
RecordContainer col = (RecordContainer)ch[i];
Record[] sr = col.getChildRecords();
for (int j = 0; j < sr.length; j++) {
if(sr[j] instanceof Sound){
lst.add(new SoundData((Sound)sr[j]));
}
}
}
}
return (SoundData[])lst.toArray(new SoundData[lst.size()]);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,80 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.model;
import junit.framework.TestCase;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hslf.usermodel.RichTextRun;
import org.apache.poi.hslf.HSLFSlideShow;
import java.awt.*;
import java.awt.Rectangle;
import java.awt.geom.*;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
/**
* Test Freeform object.
* The Freeform shape is constructed from java.awt.GeneralPath.
* Check that the get/set path accessors are consistent.
* (TODO: verification of Bezier curves is more difficult due to rounding error. Figure out a test approach for that)
*
* @author Yegor Kozlov
*/
public class TestFreeform extends TestCase {
public void testClosedPath() throws Exception {
GeneralPath path1 = new GeneralPath();
path1.moveTo(100, 100);
path1.lineTo(200, 100);
path1.lineTo(200, 200);
path1.lineTo(100, 200);
path1.closePath();
Freeform p = new Freeform();
p.setPath(path1);
GeneralPath path2 = p.getPath();
assertTrue(new Area(path1).equals(new Area(path2)));
}
public void testLine() throws Exception {
GeneralPath path1 = new GeneralPath(new Line2D.Double(100, 100, 200, 100));
Freeform p = new Freeform();
p.setPath(path1);
GeneralPath path2 = p.getPath();
assertTrue(new Area(path1).equals(new Area(path2)));
}
public void testRectangle() throws Exception {
GeneralPath path1 = new GeneralPath(new Rectangle2D.Double(100, 100, 200, 50));
Freeform p = new Freeform();
p.setPath(path1);
GeneralPath path2 = p.getPath();
assertTrue(new Area(path1).equals(new Area(path2)));
}
}

View File

@ -21,10 +21,19 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import java.io.*; import java.io.*;
import java.util.List;
import java.util.ArrayList;
import org.apache.poi.hslf.HSLFSlideShow; import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.ObjectData; import org.apache.poi.hslf.usermodel.ObjectData;
import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.PictureData;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.hwpf.usermodel.Paragraph;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -59,4 +68,41 @@ public class TestOleEmbedding extends TestCase
slideShow.close(); slideShow.close();
} }
} }
public void testOLEShape() throws Exception {
String dirname = System.getProperty("HSLF.testdata.path");
File file = new File(dirname, "ole2-embedding-2003.ppt");
FileInputStream is = new FileInputStream(file);
SlideShow ppt = new SlideShow(is);
is.close();
Slide slide = ppt.getSlides()[0];
Shape[] sh = slide.getShapes();
int cnt = 0;
for (int i = 0; i < sh.length; i++) {
if(sh[i] instanceof OLEShape){
cnt++;
OLEShape ole = (OLEShape)sh[i];
ObjectData data = ole.getObjectData();
if("Worksheet".equals(ole.getInstanceName())){
//Voila! we created a workbook from the embedded OLE data
HSSFWorkbook wb = new HSSFWorkbook(data.getData());
HSSFSheet sheet = wb.getSheetAt(0);
//verify we can access the xls data
assertEquals(1, sheet.getRow(0).getCell((short)0).getNumericCellValue(), 0);
assertEquals(1, sheet.getRow(1).getCell((short)0).getNumericCellValue(), 0);
assertEquals(2, sheet.getRow(2).getCell((short)0).getNumericCellValue(), 0);
assertEquals(3, sheet.getRow(3).getCell((short)0).getNumericCellValue(), 0);
assertEquals(8, sheet.getRow(5).getCell((short)0).getNumericCellValue(), 0);
} else if ("Document".equals(ole.getInstanceName())){
//creating a HWPF document
HWPFDocument doc = new HWPFDocument(data.getData());
String txt = doc.getRange().getParagraph(0).text();
assertEquals("OLE embedding is thoroughly unremarkable.\r", txt);
}
}
}
assertEquals("Expected 2 OLE shapes", 2, cnt);
}
} }

View File

@ -307,4 +307,22 @@ public class TestShapes extends TestCase {
sl = ppt.getSlides()[0]; sl = ppt.getSlides()[0];
assertEquals("expected 0 shaped in " + file, 0, sl.getShapes().length); 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());
}
} }

View File

@ -0,0 +1,86 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Arrays;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.SlideShow;
/**
* Tests Sound-related records: SoundCollection(2020), Sound(2022) and SoundData(2023)).
*
* @author Yegor Kozlov
*/
public class TestSound extends TestCase {
public void testRealFile() throws Exception {
String cwd = System.getProperty("HSLF.testdata.path");
FileInputStream is = new FileInputStream(new File(cwd, "sound.ppt"));
SlideShow ppt = new SlideShow(is);
is.close();
// Get the document
Document doc = ppt.getDocumentRecord();
SoundCollection soundCollection = null;
Record[] doc_ch = doc.getChildRecords();
for (int i = 0; i < doc_ch.length; i++) {
if(doc_ch[i] instanceof SoundCollection){
soundCollection = (SoundCollection)doc_ch[i];
break;
}
}
assertNotNull(soundCollection);
Sound sound = null;
Record[] sound_ch = soundCollection.getChildRecords();
int k = 0;
for (int i = 0; i < sound_ch.length; i++) {
if(sound_ch[i] instanceof Sound){
sound = (Sound)sound_ch[i];
k++;
}
}
assertNotNull(sound);
assertEquals(1, k);
assertEquals("ringin.wav", sound.getSoundName());
assertEquals(".WAV", sound.getSoundType());
assertNotNull(sound.getSoundData());
File f = new File(cwd, "ringin.wav");
int length = (int)f.length();
byte[] ref_data = new byte[length];
is = new FileInputStream(f);
is.read(ref_data);
is.close();
assertTrue(Arrays.equals(ref_data, sound.getSoundData()));
}
}

View File

@ -360,4 +360,24 @@ public class TestBugs extends TestCase {
assertTrue("No Exceptions while reading file", true); assertTrue("No Exceptions while reading file", true);
} }
/**
* Bug 41071: Will not extract text from Powerpoint TextBoxes
*/
public void test41071() throws Exception {
FileInputStream is = new FileInputStream(new File(cwd, "41071.ppt"));
SlideShow ppt = new SlideShow(is);
is.close();
Slide slide = ppt.getSlides()[0];
Shape[] sh = slide.getShapes();
assertEquals(1, sh.length);
assertTrue(sh[0] instanceof TextShape);
TextShape tx = (TextShape)sh[0];
assertEquals("Fundera, planera och involvera.", tx.getTextRun().getText());
TextRun[] run = slide.getTextRuns();
assertEquals(1, run.length);
assertEquals("Fundera, planera och involvera.", run[0].getText());
}
} }

View File

@ -437,4 +437,33 @@ public class TestPictures extends TestCase{
assertTrue(pdata instanceof WMF); assertTrue(pdata instanceof WMF);
assertEquals(Picture.WMF, pdata.getType()); 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());
}
} }

View File

@ -141,7 +141,7 @@ public class TestSlideOrdering extends TestCase {
"ROMANCE: AN ANALYSIS", "ROMANCE: AN ANALYSIS",
"AGENDA", "AGENDA",
"You are an important supplier of various items that I need", "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", "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", "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", "Your physical characteristics are strong when compared with your competition",

View File

@ -0,0 +1,63 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.usermodel;
import org.apache.poi.hslf.*;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.blip.*;
import org.apache.poi.hslf.model.*;
import junit.framework.TestCase;
import java.io.*;
import java.util.Arrays;
/**
* Test reading sound data from a ppt
*
* @author Yegor Kozlov
*/
public class TestSoundData extends TestCase{
protected File cwd;
public void setUp() throws Exception {
cwd = new File(System.getProperty("HSLF.testdata.path"));
}
/**
* Read a reference sound file from disk and compare it from the data extracted from the slide show
*/
public void testSounds() throws Exception {
//read the reference sound file
File f = new File(cwd, "ringin.wav");
int length = (int)f.length();
byte[] ref_data = new byte[length];
FileInputStream is = new FileInputStream(f);
is.read(ref_data);
is.close();
is = new FileInputStream(new File(cwd, "sound.ppt"));
SlideShow ppt = new SlideShow(is);
is.close();
SoundData[] sound = ppt.getSoundData();
assertEquals("Expected 1 sound", 1, sound.length);
assertTrue(Arrays.equals(ref_data, sound[0].getData()));
}
}

Binary file not shown.

View File

@ -878,4 +878,15 @@ public final class TestBugs extends TestCase {
} }
assertEquals(713, rowsSeen); 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);
}
} }

View File

@ -49,4 +49,23 @@ public final class TestHSSFTextbox extends TestCase{
assertEquals(HSSFTextbox.VERTICAL_ALIGNMENT_CENTER, textbox.getVerticalAlignment()); assertEquals(HSSFTextbox.VERTICAL_ALIGNMENT_CENTER, textbox.getVerticalAlignment());
} }
/**
* Excel requires at least one format run in HSSFTextbox.
* When inserting text make sure that if font is not set we must set the default one.
*/
public void testSetDeafultTextFormat() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
HSSFTextbox textbox1 = patriarch.createTextbox(new HSSFClientAnchor(0,0,0,0,(short)1,1,(short)3,3));
HSSFRichTextString rt1 = new HSSFRichTextString("Hello, World!");
assertEquals(0, rt1.numFormattingRuns());
textbox1.setString(rt1);
HSSFRichTextString rt2 = textbox1.getString();
assertEquals(1, rt2.numFormattingRuns());
assertEquals(HSSFRichTextString.NO_FONT, rt2.getFontOfFormattingRun(0));
}
} }