mirror of https://github.com/apache/poi.git
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:
parent
25e37453cc
commit
24ba833f03
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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 < 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 < 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 < 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 < 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 < 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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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){
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.*;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"),
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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! -------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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.
|
@ -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)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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.
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue