From 6406cfb2d238b7bf0bf8386c5f2eb37e0ba20def Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Fri, 28 Oct 2011 14:25:53 +0000 Subject: [PATCH] more progress with PPTX2PNG: support for gradient and texture fill, also refactored usages of colors in drawingML git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1190347 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xslf/usermodel/DrawingTextBody.java | 4 +- .../poi/xslf/usermodel/Placeholder.java | 8 +- .../poi/xslf/usermodel/RenderableShape.java | 11 + .../poi/xslf/usermodel/XMLSlideShow.java | 22 +- .../poi/xslf/usermodel/XSLFAutoShape.java | 23 - .../poi/xslf/usermodel/XSLFBackground.java | 63 +- .../apache/poi/xslf/usermodel/XSLFColor.java | 557 +++++++++++++++++- .../xslf/usermodel/XSLFCommentAuthors.java | 4 +- .../poi/xslf/usermodel/XSLFComments.java | 4 +- .../xslf/usermodel/XSLFConnectorShape.java | 17 +- .../poi/xslf/usermodel/XSLFDrawing.java | 3 +- .../poi/xslf/usermodel/XSLFGraphicFrame.java | 10 +- .../poi/xslf/usermodel/XSLFGroupShape.java | 6 +- .../poi/xslf/usermodel/XSLFImageRendener.java | 23 +- .../apache/poi/xslf/usermodel/XSLFNotes.java | 4 +- .../poi/xslf/usermodel/XSLFNotesMaster.java | 5 - .../poi/xslf/usermodel/XSLFPictureShape.java | 52 +- .../apache/poi/xslf/usermodel/XSLFShadow.java | 24 +- .../apache/poi/xslf/usermodel/XSLFShape.java | 3 +- .../apache/poi/xslf/usermodel/XSLFSheet.java | 17 +- .../poi/xslf/usermodel/XSLFSimpleShape.java | 231 ++++++-- .../apache/poi/xslf/usermodel/XSLFSlide.java | 10 +- .../poi/xslf/usermodel/XSLFSlideLayout.java | 15 +- .../poi/xslf/usermodel/XSLFSlideMaster.java | 14 +- .../apache/poi/xslf/usermodel/XSLFTable.java | 5 - .../poi/xslf/usermodel/XSLFTableCell.java | 17 +- .../poi/xslf/usermodel/XSLFTableRow.java | 5 +- .../poi/xslf/usermodel/XSLFTableStyles.java | 2 +- .../poi/xslf/usermodel/XSLFTextBox.java | 8 - .../poi/xslf/usermodel/XSLFTextParagraph.java | 33 +- .../poi/xslf/usermodel/XSLFTextRun.java | 44 +- .../poi/xslf/usermodel/XSLFTextShape.java | 22 +- .../apache/poi/xslf/usermodel/XSLFTheme.java | 170 +----- .../poi/xslf/usermodel/TestPPTX2PNG.java | 50 ++ .../poi/xslf/usermodel/TestXSLFColor.java | 154 +++++ .../xslf/usermodel/TestXSLFSimpleShape.java | 4 +- .../poi/xslf/usermodel/TestXSLFTheme.java | 4 +- .../xwpf/usermodel/TestXWPFPictureData.java | 2 +- test-data/slideshow/backgrounds.pptx | Bin 0 -> 63442 bytes 39 files changed, 1182 insertions(+), 468 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java create mode 100644 src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java create mode 100755 src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java create mode 100755 test-data/slideshow/backgrounds.pptx diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/DrawingTextBody.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/DrawingTextBody.java index 05ae0f3d1..626dbff21 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/DrawingTextBody.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/DrawingTextBody.java @@ -17,11 +17,11 @@ package org.apache.poi.xslf.usermodel; -import java.util.List; - import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; +import java.util.List; + public class DrawingTextBody { private final CTTextBody textBody; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java index 0e958015e..a96e88778 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java @@ -32,5 +32,11 @@ public enum Placeholder { HEADER, OBJECT, CHART, - TABLE + TABLE, + CLIP_ART, + DGM, + MEDIA, + SLIDE_IMAGE, + PICTURE + } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java new file mode 100644 index 000000000..393ba333e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java @@ -0,0 +1,11 @@ +package org.apache.poi.xslf.usermodel; + +/** + * Created by IntelliJ IDEA. + * User: yegor + * Date: Oct 27, 2011 + * Time: 4:50:08 PM + * To change this template use File | Settings | File Templates. + */ +class RenderableShape { +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index b376d1f78..8f25beeed 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -16,17 +16,6 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import java.awt.Dimension; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLException; @@ -51,6 +40,17 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideSize; import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument; +import java.awt.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + /** * High level representation of a ooxml slideshow. * This is the first object most users will construct whether diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java index 193e5f023..2b8a1e08b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java @@ -20,38 +20,15 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Beta; -import org.apache.poi.util.Units; -import org.apache.poi.xslf.model.geom.Context; -import org.apache.poi.xslf.model.geom.CustomGeometry; -import org.apache.poi.xslf.model.geom.Path; -import org.apache.poi.xslf.model.geom.PresetGeometries; -import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide; -import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuideList; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; -import org.openxmlformats.schemas.drawingml.x2006.main.STTextAnchoringType; -import org.openxmlformats.schemas.drawingml.x2006.main.STTextWrappingType; -import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTShapeNonVisual; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; -import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.GeneralPath; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.List; import java.util.regex.Pattern; -import java.util.regex.Matcher; /** * Represents a preset geometric shape. diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java index 646c5a40a..cd82eabea 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java @@ -17,14 +17,17 @@ package org.apache.poi.xslf.usermodel; -import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.openxml4j.opc.PackageRelationship; import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; +import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix; +import org.openxmlformats.schemas.drawingml.x2006.main.CTBackgroundFillStyleList; +import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlCursor; -import javax.imageio.ImageIO; +import javax.xml.namespace.QName; import java.awt.*; -import java.awt.image.BufferedImage; +import java.awt.geom.Rectangle2D; /** * Background shape @@ -37,34 +40,40 @@ public class XSLFBackground extends XSLFSimpleShape { super(shape, sheet); } - public void draw(Graphics2D graphics) { + @Override + public Rectangle2D getAnchor(){ Dimension pg = getSheet().getSlideShow().getPageSize(); - Rectangle anchor = new Rectangle(0, 0, pg.width, pg.height); - CTBackgroundProperties pr = ((CTBackground) getXmlObject()).getBgPr(); - if (pr == null) return; + return new Rectangle2D.Double(0, 0, pg.getWidth(), pg.getHeight()); + } - XSLFTheme theme = getSheet().getTheme(); - if (pr.isSetSolidFill()) { - Color color = theme.getSolidFillColor(pr.getSolidFill()); - graphics.setPaint(color); - graphics.fill(anchor); + public void draw(Graphics2D graphics) { + Rectangle2D anchor = getAnchor(); + + XmlObject spPr = null; + CTBackground bg = (CTBackground)getXmlObject(); + if(bg.isSetBgPr()){ + spPr = bg.getBgPr(); + } else if (bg.isSetBgRef()){ + CTStyleMatrixReference bgRef= bg.getBgRef(); + int idx = (int)bgRef.getIdx() - 1000; + XSLFTheme theme = getSheet().getTheme(); + CTBackgroundFillStyleList bgStyles = + theme.getXmlObject().getThemeElements().getFmtScheme().getBgFillStyleLst(); + + // TODO pass this to getPaint + XmlObject bgStyle = bgStyles.selectPath("*")[idx]; } - if (pr.isSetBlipFill()) { - String blipId = pr.getBlipFill().getBlip().getEmbed(); - PackagePart p = getSheet().getPackagePart(); - PackageRelationship rel = p.getRelationship(blipId); - if (rel != null) { - try { - BufferedImage img = ImageIO.read(p.getRelatedPart(rel).getInputStream()); - graphics.drawImage(img, (int) anchor.getX(), (int) anchor.getY(), - (int) anchor.getWidth(), (int) anchor.getHeight(), null); - } - catch (Exception e) { - return; - } - } + if(spPr == null){ + return; + } + + Paint fill = getPaint(graphics, spPr); + if(fill != null) { + graphics.setPaint(fill); + graphics.fill(anchor); } } + } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java index 614262a9e..8ca8dbde9 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java @@ -18,37 +18,544 @@ */ package org.apache.poi.xslf.usermodel; -import java.awt.Color; - +import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; +import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; -import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTHslColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTScRgbColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSystemColor; +import org.w3c.dom.Node; +import java.awt.*; +import java.util.HashMap; +import java.util.Map; + +/** + * Encapsulates logic to read color definitions from DrawingML and convert them to java.awt.Color + * + * @author Yegor Kozlov + */ +@Beta public class XSLFColor { - private final CTColor _ctColor; - - XSLFColor(CTColor ctColor){ - _ctColor = ctColor; - } - + private XmlObject _xmlObject; + private Color _color; + + XSLFColor(XmlObject obj, XSLFTheme theme) { + _xmlObject = obj; + _color = toColor(obj, theme); + } + @Internal - public CTColor getXmlObject() { - return _ctColor; + public XmlObject getXmlObject() { + return _xmlObject; } - - public Color getColor(){ - return getColor(0xFF); + + /** + * + * @return the displayed color as a Java Color. + * If not color information was found in the supplied xml object then a null is returned. + */ + public Color getColor() { + return _color == null ? null : applyColorTransform(_color); } - - public Color getColor(int alpha){ - Color color = Color.black; - if(_ctColor.isSetSrgbClr()){ - byte[] val = _ctColor.getSrgbClr().getVal(); - color = new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2], alpha); - } else if (_ctColor.isSetSysClr()){ - byte[] val = _ctColor.getSysClr().getLastClr(); - color = new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2], alpha); - } - return color; + + private Color applyColorTransform(Color color){ + Color result = color; + + int alpha = getAlpha(); + if(alpha != -1){ + result = new Color( + result.getRed(), result.getGreen(), result.getBlue(), + Math.round(255 * alpha * 0.01f)); + } + + int lumOff = getLumOff(); + int lumMod = getLumMod(); + if(lumMod != -1 || lumOff != -1){ + result = modulateLuminanace(result, + lumMod == -1 ? 100 : lumMod, + lumOff == -1 ? 0 : lumOff); + } + + int shade = getShade(); + if(shade != -1){ + result = shade(result, shade); + } + + int tint = getTint(); + if(tint != -1){ + result = tint(result, tint); + } + + return result; + } + + static Color toColor(XmlObject obj, XSLFTheme theme) { + Color color = null; + for (XmlObject ch : obj.selectPath("*")) { + if (ch instanceof CTHslColor) { + CTHslColor hsl = (CTHslColor)ch; + int h = hsl.getHue2(); + int s = hsl.getSat2(); + int l = hsl.getLum2(); + // is it correct ? + color = Color.getHSBColor(h / 60000f, s / 100000f, l / 100000f); + } else if (ch instanceof CTPresetColor) { + CTPresetColor prst = (CTPresetColor)ch; + String colorName = prst.getVal().toString(); + color = presetColors.get(colorName); + } else if (ch instanceof CTSchemeColor) { + CTSchemeColor schemeColor = (CTSchemeColor)ch; + String colorRef = schemeColor.getVal().toString(); + // find referenced CTColor in the theme and convert it to java.awt.Color via a recursive call + CTColor ctColor = theme.getCTColor(colorRef); + if(ctColor != null) color = toColor(ctColor, null); + else { + color = Color.black; + } + } else if (ch instanceof CTScRgbColor) { + // same as CTSRgbColor but with values expressed in percents + CTScRgbColor scrgb = (CTScRgbColor)ch; + int r = scrgb.getR(); + int g = scrgb.getG(); + int b = scrgb.getB(); + color = new Color(255 * r / 100000, 255 * g / 100000, 255 * b / 100000); + } else if (ch instanceof CTSRgbColor) { + CTSRgbColor srgb = (CTSRgbColor)ch; + byte[] val = srgb.getVal(); + color = new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2]); + } else if (ch instanceof CTSystemColor) { + CTSystemColor sys = (CTSystemColor)ch; + if(sys.isSetLastClr()) { + byte[] val = sys.getLastClr(); + color = new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2]); + } else { + // YK: color is a string like "menuText" or "windowText", we return black for such cases + String colorName = sys.getVal().toString(); + color = Color.black; + } + } else { + throw new IllegalArgumentException("Unexpected color choice: " + ch.getClass()); + } + } + return color; + } + + private int getPercentageValue(String elem){ + XmlObject[] obj = _xmlObject.selectPath( + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem); + if(obj.length == 1){ + Node attr = obj[0].getDomNode().getAttributes().getNamedItem("val"); + if(attr != null) { + return Integer.parseInt(attr.getNodeValue()) / 1000; + } + } + return -1; + } + + private int getAngleValue(String elem){ + XmlObject[] obj = _xmlObject.selectPath( + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem); + if(obj.length == 1){ + Node attr = obj[0].getDomNode().getAttributes().getNamedItem("val"); + if(attr != null) { + return Integer.parseInt(attr.getNodeValue()) / 60000; + } + } + return -1; + } + + /** + * the opacity as expressed by a percentage value + * + * @return opacity in percents in the range [0..100] + * or -1 if the value is not set + */ + int getAlpha(){ + return getPercentageValue("alpha"); + } + + /** + * the opacity as expressed by a percentage relative to the input color + * + * @return opacity in percents in the range [0..100] + * or -1 if the value is not set + */ + int getAlphaMod(){ + return getPercentageValue("alphaMod"); + } + + /** + * the opacity as expressed by a percentage offset increase or decrease relative to + * the input color. Increases will never increase the opacity beyond 100%, decreases will + * never decrease the opacity below 0%. + * + * @return opacity shift in percents in the range [0..100] + * or -1 if the value is not set + */ + int getAlphaOff(){ + return getPercentageValue("alphaOff"); + } + + + int getHue(){ + return getAngleValue("hue"); + } + + int getHueMod(){ + return getPercentageValue("hueMod"); + } + + int getHueOff(){ + return getPercentageValue("hueOff"); + } + + /** + * specifies the input color with the specified luminance, + * but with its hue and saturation unchanged. + * + * @return luminance in percents in the range [0..100] + * or -1 if the value is not set + */ + int getLum(){ + return getPercentageValue("lum"); + } + + /** + * the luminance as expressed by a percentage relative to the input color + * + * @return luminance in percents in the range [0..100] + * or -1 if the value is not set + */ + int getLumMod(){ + return getPercentageValue("lumMod"); + } + + /** + * the luminance shift as expressed by a percentage relative to the input color + * + * @return luminance shift in percents in the range [0..100] + * or -1 if the value is not set + */ + int getLumOff(){ + return getPercentageValue("lumOff"); + } + + /** + * specifies the input color with the specified saturation, + * but with its hue and luminance unchanged. + * + * @return saturation in percents in the range [0..100] + * or -1 if the value is not set + */ + int getSat(){ + return getPercentageValue("sat"); + } + + /** + * the saturation as expressed by a percentage relative to the input color + * + * @return saturation in percents in the range [0..100] + * or -1 if the value is not set + */ + int getSatMod(){ + return getPercentageValue("satMod"); + } + + /** + * the saturation shift as expressed by a percentage relative to the input color + * + * @return saturation shift in percents in the range [0..100] + * or -1 if the value is not set + */ + int getSatOff(){ + return getPercentageValue("satOff"); + } + + /** + * specifies the input color with the specific red component, but with the blue and green color + * components unchanged + * + * @return the value of the red component specified as a + * percentage with 0% indicating minimal blue and 100% indicating maximum + * or -1 if the value is not set + */ + int getRed(){ + return getPercentageValue("red"); + } + + int getRedMod(){ + return getPercentageValue("redMod"); + } + + int getRedOff(){ + return getPercentageValue("redOff"); + } + + /** + * specifies the input color with the specific green component, but with the red and blue color + * components unchanged + * + * @return the value of the green component specified as a + * percentage with 0% indicating minimal blue and 100% indicating maximum + * or -1 if the value is not set + */ + int getGreen(){ + return getPercentageValue("green"); + } + + int getGreenMod(){ + return getPercentageValue("greenMod"); + } + + int getGreenOff(){ + return getPercentageValue("greenOff"); + } + + /** + * specifies the input color with the specific blue component, but with the red and green color + * components unchanged + * + * @return the value of the blue component specified as a + * percentage with 0% indicating minimal blue and 100% indicating maximum + * or -1 if the value is not set + */ + int getBlue(){ + return getPercentageValue("blue"); + } + + int getBlueMod(){ + return getPercentageValue("blueMod"); + } + + int getBlueOff(){ + return getPercentageValue("blueOff"); + } + + /** + * specifies a darker version of its input color. + * A 10% shade is 10% of the input color combined with 90% black. + * + * @return the value of the shade specified as a + * percentage with 0% indicating minimal blue and 100% indicating maximum + * or -1 if the value is not set + */ + int getShade(){ + return getPercentageValue("shade"); + } + + /** + * specifies a lighter version of its input color. + * A 10% tint is 10% of the input color combined with 90% white. + * + * @return the value of the tint specified as a + * percentage with 0% indicating minimal blue and 100% indicating maximum + * or -1 if the value is not set + */ + int getTint(){ + return getPercentageValue("tint"); + } + + + /** + * Apply lumMod / lumOff adjustments + * + * @param c the color to modify + * @param lumMod luminance modulation in the range [0..100] + * @param lumOff luminance offset in the range [0..100] + * @return modified color + */ + private static Color modulateLuminanace(Color c, int lumMod, int lumOff) { + Color color; + if (lumOff > 0) { + color = new Color( + (int) (Math.round((255 - c.getRed()) * (100.0 - lumMod) / 100.0 + c.getRed())), + (int) (Math.round((255 - c.getGreen()) * lumOff / 100.0 + c.getGreen())), + (int) (Math.round((255 - c.getBlue()) * lumOff / 100.0 + c.getBlue())), + c.getAlpha() + ); + } else { + color = new Color( + (int) (Math.round(c.getRed() * lumMod / 100.0)), + (int) (Math.round(c.getGreen() * lumMod / 100.0)), + (int) (Math.round(c.getBlue() * lumMod / 100.0)), + c.getAlpha() + ); + } + return color; + } + + private static Color shade(Color c, int shade) { + return new Color( + (int)(c.getRed() * shade * 0.01), + (int)(c.getGreen() * shade * 0.01), + (int)(c.getBlue() * shade * 0.01), + c.getAlpha()); + } + + private static Color tint(Color c, int tint) { + int r = c.getRed(); + int g = c.getGreen(); + int b = c.getBlue(); + + float ftint = tint / 100.0f; + + int red = Math.round(ftint * r + (1 - ftint) * 255); + int green = Math.round(ftint * g + (1 - ftint) * 255); + int blue = Math.round(ftint * b + (1 - ftint) * 255); + + return new Color(red, green, blue); + } + + /** + * Preset colors defined in DrawingML + */ + static Map presetColors; + + static { + presetColors = new HashMap(); + presetColors.put("aliceBlue", new Color(240, 248, 255)); + presetColors.put("antiqueWhite", new Color(250, 235, 215)); + presetColors.put("aqua", new Color(0, 255, 255)); + presetColors.put("aquamarine", new Color(127, 255, 212)); + presetColors.put("azure", new Color(240, 255, 255)); + presetColors.put("beige", new Color(245, 245, 220)); + presetColors.put("bisque", new Color(255, 228, 196)); + presetColors.put("black", new Color(0, 0, 0)); + presetColors.put("blanchedAlmond", new Color(255, 235, 205)); + presetColors.put("blue", new Color(0, 0, 255)); + presetColors.put("blueViolet", new Color(138, 43, 226)); + presetColors.put("brown", new Color(165, 42, 42)); + presetColors.put("burlyWood", new Color(222, 184, 135)); + presetColors.put("cadetBlue", new Color(95, 158, 160)); + presetColors.put("chartreuse", new Color(127, 255, 0)); + presetColors.put("chocolate", new Color(210, 105, 30)); + presetColors.put("coral", new Color(255, 127, 80)); + presetColors.put("cornflowerBlue", new Color(100, 149, 237)); + presetColors.put("crimson", new Color(220, 20, 60)); + presetColors.put("cyan", new Color(0, 255, 255)); + presetColors.put("deepPink", new Color(255, 20, 147)); + presetColors.put("deepSkyBlue", new Color(0, 191, 255)); + presetColors.put("dimGray", new Color(105, 105, 105)); + presetColors.put("dkBlue", new Color(0, 0, 139)); + presetColors.put("dkCyan", new Color(0, 139, 139)); + presetColors.put("dkGoldenrod", new Color(184, 134, 11)); + presetColors.put("dkGray", new Color(169, 169, 169)); + presetColors.put("dkGreen", new Color(0, 100, 0)); + presetColors.put("dkKhaki", new Color(189, 183, 107)); + presetColors.put("dkMagenta", new Color(139, 0, 139)); + presetColors.put("dkOliveGreen", new Color(85, 107, 47)); + presetColors.put("dkOrange", new Color(255, 140, 0)); + presetColors.put("dkOrchid", new Color(153, 50, 204)); + presetColors.put("dkRed", new Color(139, 0, 0)); + presetColors.put("dkSalmon", new Color(233, 150, 122)); + presetColors.put("dkSeaGreen", new Color(143, 188, 139)); + presetColors.put("dkSlateBlue", new Color(72, 61, 139)); + presetColors.put("dkSlateGray", new Color(47, 79, 79)); + presetColors.put("dkTurquoise", new Color(0, 206, 209)); + presetColors.put("dkViolet", new Color(148, 0, 211)); + presetColors.put("dodgerBlue", new Color(30, 144, 255)); + presetColors.put("firebrick", new Color(178, 34, 34)); + presetColors.put("floralWhite", new Color(255, 250, 240)); + presetColors.put("forestGreen", new Color(34, 139, 34)); + presetColors.put("fuchsia", new Color(255, 0, 255)); + presetColors.put("gainsboro", new Color(220, 220, 220)); + presetColors.put("ghostWhite", new Color(248, 248, 255)); + presetColors.put("gold", new Color(255, 215, 0)); + presetColors.put("goldenrod", new Color(218, 165, 32)); + presetColors.put("gray", new Color(128, 128, 128)); + presetColors.put("green", new Color(0, 128, 0)); + presetColors.put("greenYellow", new Color(173, 255, 47)); + presetColors.put("honeydew", new Color(240, 255, 240)); + presetColors.put("hotPink", new Color(255, 105, 180)); + presetColors.put("indianRed", new Color(205, 92, 92)); + presetColors.put("indigo", new Color(75, 0, 130)); + presetColors.put("ivory", new Color(255, 255, 240)); + presetColors.put("khaki", new Color(240, 230, 140)); + presetColors.put("lavender", new Color(230, 230, 250)); + presetColors.put("lavenderBlush", new Color(255, 240, 245)); + presetColors.put("lawnGreen", new Color(124, 252, 0)); + presetColors.put("lemonChiffon", new Color(255, 250, 205)); + presetColors.put("lime", new Color(0, 255, 0)); + presetColors.put("limeGreen", new Color(50, 205, 50)); + presetColors.put("linen", new Color(250, 240, 230)); + presetColors.put("ltBlue", new Color(173, 216, 230)); + presetColors.put("ltCoral", new Color(240, 128, 128)); + presetColors.put("ltCyan", new Color(224, 255, 255)); + presetColors.put("ltGoldenrodYellow", new Color(250, 250, 120)); + presetColors.put("ltGray", new Color(211, 211, 211)); + presetColors.put("ltGreen", new Color(144, 238, 144)); + presetColors.put("ltPink", new Color(255, 182, 193)); + presetColors.put("ltSalmon", new Color(255, 160, 122)); + presetColors.put("ltSeaGreen", new Color(32, 178, 170)); + presetColors.put("ltSkyBlue", new Color(135, 206, 250)); + presetColors.put("ltSlateGray", new Color(119, 136, 153)); + presetColors.put("ltSteelBlue", new Color(176, 196, 222)); + presetColors.put("ltYellow", new Color(255, 255, 224)); + presetColors.put("magenta", new Color(255, 0, 255)); + presetColors.put("maroon", new Color(128, 0, 0)); + presetColors.put("medAquamarine", new Color(102, 205, 170)); + presetColors.put("medBlue", new Color(0, 0, 205)); + presetColors.put("medOrchid", new Color(186, 85, 211)); + presetColors.put("medPurple", new Color(147, 112, 219)); + presetColors.put("medSeaGreen", new Color(60, 179, 113)); + presetColors.put("medSlateBlue", new Color(123, 104, 238)); + presetColors.put("medSpringGreen", new Color(0, 250, 154)); + presetColors.put("medTurquoise", new Color(72, 209, 204)); + presetColors.put("medVioletRed", new Color(199, 21, 133)); + presetColors.put("midnightBlue", new Color(25, 25, 112)); + presetColors.put("mintCream", new Color(245, 255, 250)); + presetColors.put("mistyRose", new Color(255, 228, 225)); + presetColors.put("moccasin", new Color(255, 228, 181)); + presetColors.put("navajoWhite", new Color(255, 222, 173)); + presetColors.put("navy", new Color(0, 0, 128)); + presetColors.put("oldLace", new Color(253, 245, 230)); + presetColors.put("olive", new Color(128, 128, 0)); + presetColors.put("oliveDrab", new Color(107, 142, 35)); + presetColors.put("orange", new Color(255, 165, 0)); + presetColors.put("orangeRed", new Color(255, 69, 0)); + presetColors.put("orchid", new Color(218, 112, 214)); + presetColors.put("paleGoldenrod", new Color(238, 232, 170)); + presetColors.put("paleGreen", new Color(152, 251, 152)); + presetColors.put("paleTurquoise", new Color(175, 238, 238)); + presetColors.put("paleVioletRed", new Color(219, 112, 147)); + presetColors.put("papayaWhip", new Color(255, 239, 213)); + presetColors.put("peachPuff", new Color(255, 218, 185)); + presetColors.put("peru", new Color(205, 133, 63)); + presetColors.put("pink", new Color(255, 192, 203)); + presetColors.put("plum", new Color(221, 160, 221)); + presetColors.put("powderBlue", new Color(176, 224, 230)); + presetColors.put("purple", new Color(128, 0, 128)); + presetColors.put("red", new Color(255, 0, 0)); + presetColors.put("rosyBrown", new Color(188, 143, 143)); + presetColors.put("royalBlue", new Color(65, 105, 225)); + presetColors.put("saddleBrown", new Color(139, 69, 19)); + presetColors.put("salmon", new Color(250, 128, 114)); + presetColors.put("sandyBrown", new Color(244, 164, 96)); + presetColors.put("seaGreen", new Color(46, 139, 87)); + presetColors.put("seaShell", new Color(255, 245, 238)); + presetColors.put("sienna", new Color(160, 82, 45)); + presetColors.put("silver", new Color(192, 192, 192)); + presetColors.put("skyBlue", new Color(135, 206, 235)); + presetColors.put("slateBlue", new Color(106, 90, 205)); + presetColors.put("slateGray", new Color(112, 128, 144)); + presetColors.put("snow", new Color(255, 250, 250)); + presetColors.put("springGreen", new Color(0, 255, 127)); + presetColors.put("steelBlue", new Color(70, 130, 180)); + presetColors.put("tan", new Color(210, 180, 140)); + presetColors.put("teal", new Color(0, 128, 128)); + presetColors.put("thistle", new Color(216, 191, 216)); + presetColors.put("tomato", new Color(255, 99, 71)); + presetColors.put("turquoise", new Color(64, 224, 208)); + presetColors.put("violet", new Color(238, 130, 238)); + presetColors.put("wheat", new Color(245, 222, 179)); + presetColors.put("white", new Color(255, 255, 255)); + presetColors.put("whiteSmoke", new Color(245, 245, 245)); + presetColors.put("yellow", new Color(255, 255, 0)); + presetColors.put("yellowGreen", new Color(154, 205, 50)); } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFCommentAuthors.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFCommentAuthors.java index 231dc008d..23ddb4d53 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFCommentAuthors.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFCommentAuthors.java @@ -17,8 +17,6 @@ package org.apache.poi.xslf.usermodel; -import java.io.IOException; - import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; @@ -28,6 +26,8 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTCommentAuthor; import org.openxmlformats.schemas.presentationml.x2006.main.CTCommentAuthorList; import org.openxmlformats.schemas.presentationml.x2006.main.CmAuthorLstDocument; +import java.io.IOException; + @Beta public class XSLFCommentAuthors extends POIXMLDocumentPart { private final CTCommentAuthorList _authors; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFComments.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFComments.java index 35e884658..9eed7ec7e 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFComments.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFComments.java @@ -17,8 +17,6 @@ package org.apache.poi.xslf.usermodel; -import java.io.IOException; - import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; @@ -28,6 +26,8 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTComment; import org.openxmlformats.schemas.presentationml.x2006.main.CTCommentList; import org.openxmlformats.schemas.presentationml.x2006.main.CmLstDocument; +import java.io.IOException; + @Beta public class XSLFComments extends POIXMLDocumentPart { private final CTCommentList _comments; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java index cc16d26eb..2e28774a4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java @@ -20,20 +20,23 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Beta; +import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; -import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndLength; import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndType; import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndWidth; -import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndLength; +import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnectorNonVisual; import java.awt.*; -import java.awt.geom.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Rectangle2D; /** * Specifies a connection shape. @@ -202,11 +205,11 @@ public class XSLFConnectorShape extends XSLFSimpleShape { XSLFShadow shadow = getShadow(); //border - Color lineColor = getLineColor(); - if (lineColor != null) { + Paint line = getLinePaint(graphics); + if (line != null) { if (shadow != null) shadow.draw(graphics); - graphics.setColor(lineColor); + graphics.setPaint(line); applyStroke(graphics); graphics.draw(outline); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java index e731a632d..6de6e4d77 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java @@ -16,15 +16,14 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; -import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import java.awt.*; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java index 9077e256f..8e6809bba 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java @@ -19,20 +19,14 @@ package org.apache.poi.xslf.usermodel; -import org.apache.poi.sl.usermodel.Shape; -import org.apache.poi.sl.usermodel.ShapeContainer; -import org.apache.poi.sl.usermodel.ShapeGroup; import org.apache.poi.util.Beta; import org.apache.poi.util.Units; -import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; -import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; +import java.awt.*; import java.awt.geom.Rectangle2D; /** diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java index 3d5b2f6a5..95826af0e 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java @@ -22,9 +22,6 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.TargetMode; -import org.apache.poi.sl.usermodel.Shape; -import org.apache.poi.sl.usermodel.ShapeContainer; -import org.apache.poi.sl.usermodel.ShapeGroup; import org.apache.poi.util.Beta; import org.apache.poi.util.Units; import org.apache.xmlbeans.XmlObject; @@ -33,13 +30,12 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.List; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java index 46fa1bf17..8f763622f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java @@ -22,10 +22,12 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Beta; import javax.imageio.ImageIO; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; /** * For now this class renders only images supported by the javax.imageio.ImageIO @@ -74,14 +76,25 @@ public class XSLFImageRendener { public boolean drawImage(Graphics2D graphics, XSLFPictureData data, Rectangle2D anchor) { try { - BufferedImage img = ImageIO.read(new ByteArrayInputStream(data - .getData())); - graphics.drawImage(img, (int) anchor.getX(), (int) anchor.getY(), - (int) anchor.getWidth(), (int) anchor.getHeight(), null); + BufferedImage img = readImage(new ByteArrayInputStream(data.getData())); + if (img != null){ + graphics.drawImage(img, (int) anchor.getX(), (int) anchor.getY(), + (int) anchor.getWidth(), (int) anchor.getHeight(), null); + } return true; } catch (Exception e) { return false; } } + + /** + * create a buffered image from input stream + * + * @return a BufferedImage containing the decoded + * contents of the input, or null. + */ + public BufferedImage readImage(InputStream is) throws IOException { + return ImageIO.read(is); + } } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java index d4508f5ea..3025afcc4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java @@ -16,8 +16,6 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import java.io.IOException; - import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; @@ -26,6 +24,8 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTCommonSlideData; import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesSlide; import org.openxmlformats.schemas.presentationml.x2006.main.NotesDocument; +import java.io.IOException; + @Beta public final class XSLFNotes extends XSLFSheet { private CTNotesSlide _notes; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java index 32bf5ac2d..05813bdb9 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java @@ -16,19 +16,14 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; -import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMaster; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; import org.openxmlformats.schemas.presentationml.x2006.main.NotesMasterDocument; -import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; import java.io.IOException; -import java.util.HashMap; import java.util.Map; /** diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java index c5c92b5dc..0ceb5c563 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java @@ -19,27 +19,21 @@ package org.apache.poi.xslf.usermodel; -import org.apache.poi.POIXMLDocumentPart; -import org.apache.poi.sl.usermodel.ShapeContainer; +import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; -import org.apache.poi.util.POILogger; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; import org.openxmlformats.schemas.presentationml.x2006.main.CTPictureNonVisual; import javax.imageio.ImageIO; - -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Rectangle; +import java.awt.*; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -104,9 +98,15 @@ public class XSLFPictureShape extends XSLFSimpleShape { CTPicture ct = (CTPicture)getXmlObject(); String blipId = ct.getBlipFill().getBlip().getEmbed(); - for (POIXMLDocumentPart part : getSheet().getRelations()) { - if(part.getPackageRelationship().getId().equals(blipId)){ - _data = (XSLFPictureData)part; + PackagePart p = getSheet().getPackagePart(); + PackageRelationship rel = p.getRelationship(blipId); + if (rel != null) { + try { + PackagePart imgPart = p.getRelatedPart(rel); + _data = new XSLFPictureData(imgPart, rel); + } + catch (Exception e) { + throw new POIXMLException(e); } } } @@ -120,31 +120,31 @@ public class XSLFPictureShape extends XSLFSimpleShape { // shadow XSLFShadow shadow = getShadow(); - //fill - Color fillColor = getFillColor(); - if (fillColor != null) { - if(shadow != null) shadow.draw(graphics); + Paint fill = getFill(graphics); + Paint line = getLinePaint(graphics); + if(shadow != null) { + shadow.draw(graphics); + } - graphics.setColor(fillColor); - applyFill(graphics); + if(fill != null) { + graphics.setPaint(fill); graphics.fill(outline); } - + + XSLFPictureData data = getPictureData(); if(data == null) return; XSLFImageRendener renderer = (XSLFImageRendener)graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER); if(renderer == null) renderer = new XSLFImageRendener(); - + renderer.drawImage(graphics, data, getAnchor()); - //border overlays the image - Color lineColor = getLineColor(); - if (lineColor != null){ - graphics.setColor(lineColor); + if (line != null){ + graphics.setPaint(line); applyStroke(graphics); graphics.draw(outline); } } -} \ No newline at end of file +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java index 837529a90..8838fa3e4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java @@ -17,19 +17,11 @@ package org.apache.poi.xslf.usermodel; -import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Units; -import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; -import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTOuterShadowEffect; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetColor; -import javax.imageio.ImageIO; import java.awt.*; import java.awt.geom.Rectangle2D; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; /** * Represents a shadow of a shape. For now supports only outer shadows. @@ -49,8 +41,8 @@ public class XSLFShadow extends XSLFSimpleShape { public void draw(Graphics2D graphics) { Shape outline = _parent.getOutline(); - Color parentFillColor = _parent.getFillColor(); - Color parentLineColor = _parent.getLineColor(); + Paint parentFillColor = _parent.getFill(graphics); + Paint parentLineColor = _parent.getLinePaint(graphics); double angle = getAngle(); double dist = getDistance(); @@ -120,16 +112,6 @@ public class XSLFShadow extends XSLFSimpleShape { public Color getFillColor() { XSLFTheme theme = getSheet().getTheme(); CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject(); - if(ct.isSetSchemeClr()) { - return theme.getSchemeColor(ct.getSchemeClr()); - } - else if (ct.isSetPrstClr()) { - return theme.getPresetColor(ct.getPrstClr()); - } - else if (ct.isSetSrgbClr()) { - return theme.getSrgbColor(ct.getSrgbClr()); - } - - return null; + return ct == null ? null : new XSLFColor(ct, theme).getColor(); } } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index 2c676caf4..992973c51 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -21,9 +21,8 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlObject; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.Rectangle2D; /** diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java index f4a46fc90..87da995d4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -21,22 +21,20 @@ import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.util.Beta; -import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; import org.openxmlformats.schemas.presentationml.x2006.main.CTCommonSlideData; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; -import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; +import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import javax.xml.namespace.QName; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.AffineTransform; import java.io.IOException; import java.io.OutputStream; @@ -64,7 +62,14 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { } public XMLSlideShow getSlideShow() { - return (XMLSlideShow)getParent(); + POIXMLDocumentPart p = getParent(); + while(p != null) { + if(p instanceof XMLSlideShow){ + return (XMLSlideShow)p; + } + p = p.getParent(); + } + return null; } protected List buildShapes(CTGroupShape spTree){ diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java index 049a2d321..10bad70d0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -19,22 +19,39 @@ package org.apache.poi.xslf.usermodel; -import org.apache.poi.xslf.model.geom.*; -import org.apache.poi.xslf.usermodel.LineCap; -import org.apache.poi.xslf.usermodel.LineDash; -import org.apache.poi.xslf.model.PropertyFetcher; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; import org.apache.poi.util.Units; +import org.apache.poi.xslf.model.PropertyFetcher; +import org.apache.poi.xslf.model.geom.Context; +import org.apache.poi.xslf.model.geom.CustomGeometry; +import org.apache.poi.xslf.model.geom.Guide; +import org.apache.poi.xslf.model.geom.IAdjustableShape; +import org.apache.poi.xslf.model.geom.Path; +import org.apache.poi.xslf.model.geom.PresetGeometries; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.*; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; -import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; -import java.util.ArrayList; +import java.awt.image.BufferedImage; +import java.awt.Paint; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.TexturePaint; +import java.awt.AlphaComposite; +import java.awt.GradientPaint; +import java.awt.BasicStroke; +import java.awt.Stroke; +import java.util.Arrays; +import java.util.Comparator; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Constructor; /** * @author Yegor Kozlov @@ -265,40 +282,45 @@ public abstract class XSLFSimpleShape extends XSLFShape { } public Color getLineColor() { + Paint paint = getLinePaint(null); + if(paint instanceof Color){ + return (Color)paint; + } + return null; + } + + public Paint getLinePaint(final Graphics2D graphics) { final XSLFTheme theme = _sheet.getTheme(); - final Color noline = new Color(0,0,0,0); - PropertyFetcher fetcher = new PropertyFetcher(){ + final Color nofill = new Color(0,0,0,0); + PropertyFetcher fetcher = new PropertyFetcher(){ public boolean fetch(XSLFSimpleShape shape){ - CTShapeProperties spPr = shape.getSpPr(); - CTLineProperties ln = spPr.getLn(); - if (ln != null) { - if (ln.isSetNoFill()) { - setValue(noline); + CTLineProperties spPr = shape.getSpPr().getLn(); + if (spPr != null) { + if (spPr.isSetNoFill()) { + setValue(nofill); // use it as 'nofill' value return true; } - CTSolidColorFillProperties solidLine = ln.getSolidFill(); - if (solidLine != null) { - setValue( theme.getSolidFillColor(ln.getSolidFill()) ); + Paint paint = getPaint(graphics, spPr); + if (paint != null) { + setValue( paint ); return true; } } return false; + } }; fetchShapeProperty(fetcher); - Color color = fetcher.getValue(); + Paint color = fetcher.getValue(); if(color == null){ // line color was not found, check if it is defined in the theme CTShapeStyle style = getSpStyle(); if (style != null) { - CTSchemeColor schemeColor = style.getLnRef().getSchemeClr(); - if (schemeColor != null) { - color = theme.getSchemeColor(schemeColor); - } + color = new XSLFColor(style.getLnRef(), theme).getColor(); } } - return color == noline ? null : color; + return color == nofill ? null : color; } public void setLineWidth(double width) { @@ -466,17 +488,31 @@ public abstract class XSLFSimpleShape extends XSLFShape { * @return solid fill color of null if not set */ public Color getFillColor() { + Paint paint = getFill(null); + if(paint instanceof Color){ + return (Color)paint; + } + return null; + } + + /** + * fetch shape fill as a java.awt.Paint + * + * @return either Color or GradientPaint or TexturePaint or null + */ + Paint getFill(final Graphics2D graphics) { final XSLFTheme theme = _sheet.getTheme(); - final Color nofill = new Color(0,0,0,0); - PropertyFetcher fetcher = new PropertyFetcher(){ + final Color nofill = new Color(0xFF,0xFF,0xFF, 0); + PropertyFetcher fetcher = new PropertyFetcher(){ public boolean fetch(XSLFSimpleShape shape){ CTShapeProperties spPr = shape.getSpPr(); if (spPr.isSetNoFill()) { setValue(nofill); // use it as 'nofill' value return true; } - if (spPr.isSetSolidFill()) { - setValue( theme.getSolidFillColor(spPr.getSolidFill()) ); + Paint paint = getPaint(graphics, spPr); + if (paint != null) { + setValue( paint ); return true; } return false; @@ -484,18 +520,15 @@ public abstract class XSLFSimpleShape extends XSLFShape { }; fetchShapeProperty(fetcher); - Color color = fetcher.getValue(); - if(color == null){ + Paint paint = fetcher.getValue(); + if(paint == null){ // fill color was not found, check if it is defined in the theme CTShapeStyle style = getSpStyle(); if (style != null) { - CTSchemeColor schemeColor = style.getFillRef().getSchemeClr(); - if (schemeColor != null) { - color = theme.getSchemeColor(schemeColor); - } + paint = new XSLFColor(style.getFillRef(), theme).getColor(); } } - return color == nofill ? null : color; + return paint == nofill ? null : paint; } public XSLFShadow getShadow(){ @@ -532,8 +565,130 @@ public abstract class XSLFSimpleShape extends XSLFShape { } - protected void applyFill(Graphics2D graphics) { + @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated + protected Paint getPaint(Graphics2D graphics, XmlObject spPr) { + XSLFTheme theme = getSheet().getTheme(); + Rectangle2D anchor = getAnchor(); + Paint paint = null; + for(XmlObject obj : spPr.selectPath("*")){ + if(obj instanceof CTNoFillProperties){ + paint = null; + break; + } + if(obj instanceof CTSolidColorFillProperties){ + CTSolidColorFillProperties solidFill = (CTSolidColorFillProperties)obj; + XSLFColor c = new XSLFColor(solidFill, theme); + paint = c.getColor(); + } + if(obj instanceof CTBlipFillProperties){ + CTBlipFillProperties blipFill = (CTBlipFillProperties)obj; + CTBlip blip = blipFill.getBlip(); + String blipId = blip.getEmbed(); + PackagePart p = getSheet().getPackagePart(); + PackageRelationship rel = p.getRelationship(blipId); + if (rel != null) { + XSLFImageRendener renderer = null; + if(graphics != null) renderer = (XSLFImageRendener)graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER); + if(renderer == null) renderer = new XSLFImageRendener(); + + try { + BufferedImage img = renderer.readImage(p.getRelatedPart(rel).getInputStream()); + if(blip.sizeOfAlphaModFixArray() > 0){ + float alpha = blip.getAlphaModFixArray(0).getAmt()/100000.f; + AlphaComposite ac = AlphaComposite.getInstance( + AlphaComposite.SRC_OVER, alpha); + if(graphics != null) graphics.setComposite(ac); + } + + paint = new TexturePaint( + img, new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight())); + } + catch (Exception e) { + return null; + } + } + } + if(obj instanceof CTGradientFillProperties){ + CTGradientFillProperties gradFill = (CTGradientFillProperties)obj; + double angle; + if(gradFill.isSetLin()) { + angle = gradFill.getLin().getAng() / 60000; + } else { + // XSLF only supports linear gradient fills. Other types are filled as liner with angle=90 degrees + angle = 90; + } + CTGradientStop[] gs = gradFill.getGsLst().getGsArray(); + + Arrays.sort(gs, new Comparator(){ + public int compare(CTGradientStop o1, CTGradientStop o2){ + Integer pos1 = o1.getPos(); + Integer pos2 = o2.getPos(); + return pos1.compareTo(pos2); + } + }); + + Color[] colors = new Color[gs.length]; + float[] fractions = new float[gs.length]; + + AffineTransform at = AffineTransform.getRotateInstance( + Math.toRadians(angle), + anchor.getX() + anchor.getWidth()/2, + anchor.getY() + anchor.getHeight()/2); + + double diagonal = Math.sqrt(anchor.getHeight()*anchor.getHeight() + anchor.getWidth()*anchor.getWidth()); + Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth()/2 - diagonal/2, + anchor.getY() + anchor.getHeight()/2); + p1 = at.transform(p1, null); + + Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight()/2); + p2 = at.transform(p2, null); + + norm(p1, anchor); + norm(p2, anchor); + + for(int i = 0; i < gs.length; i++){ + CTGradientStop stop = gs[i]; + colors[i] = new XSLFColor(stop, theme).getColor(); + fractions[i] = stop.getPos() / 100000.f; + } + + paint = createGradientPaint(p1, p2, fractions, colors); + } + } + return paint; + } + + /** + * Trick to return GradientPaint on JDK 1.5 and LinearGradientPaint on JDK 1.6+ + */ + private Paint createGradientPaint(Point2D p1, Point2D p2, float[] fractions, Color[] colors){ + Paint paint; + try { + Class clz = Class.forName("java.awt.LinearGradientPaint"); + Constructor c = + clz.getConstructor(Point2D.class, Point2D.class, float[].class, Color[].class); + paint = (Paint)c.newInstance(p1, p2, fractions, colors); + } catch (ClassNotFoundException e){ + paint = new GradientPaint(p1, colors[0], p2, colors[colors.length - 1]); + } catch (Exception e){ + throw new RuntimeException(e); + } + return paint; + } + + void norm(Point2D p, Rectangle2D anchor){ + if(p.getX() < anchor.getX()){ + p.setLocation(anchor.getX(), p.getY()); + } else if(p.getX() > (anchor.getX() + anchor.getWidth())){ + p.setLocation(anchor.getX() + anchor.getWidth(), p.getY()); + } + + if(p.getY() < anchor.getY()){ + p.setLocation(p.getX(), anchor.getY()); + } else if (p.getY() > (anchor.getY() + anchor.getHeight())){ + p.setLocation(p.getX(), anchor.getY() + anchor.getHeight()); + } } protected float[] getDashPattern(LineDash lineDash, float lineWidth) { @@ -665,7 +820,13 @@ public abstract class XSLFSimpleShape extends XSLFShape { @Override protected java.awt.Shape getOutline(){ PresetGeometries dict = PresetGeometries.getInstance(); - String name = getSpPr().getPrstGeom().getPrst().toString(); + CTShapeProperties spPr = getSpPr(); + String name; + if(spPr.isSetPrstGeom()) { + name = spPr.getPrstGeom().getPrst().toString(); + } else { + name = "rect"; + } CustomGeometry geom = dict.get(name); Rectangle2D anchor = getAnchor(); if(geom != null) { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java index d9f7c88e6..f6f92d4f5 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java @@ -16,28 +16,24 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import java.awt.*; -import java.io.IOException; - import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlException; -import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupShapeProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; import org.openxmlformats.schemas.presentationml.x2006.main.CTCommonSlideData; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; import org.openxmlformats.schemas.presentationml.x2006.main.SldDocument; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; + +import java.awt.*; +import java.io.IOException; @Beta public final class XSLFSlide extends XSLFSheet { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java index ab6661125..699d15334 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java @@ -22,18 +22,12 @@ import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlException; -import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideLayout; import org.openxmlformats.schemas.presentationml.x2006.main.SldLayoutDocument; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; import java.io.IOException; -import java.util.Map; -import java.util.HashMap; -import java.awt.*; -import java.awt.geom.AffineTransform; @Beta public class XSLFSlideLayout extends XSLFSheet { @@ -106,9 +100,6 @@ public class XSLFSlideLayout extends XSLFSheet { return _master; } - public XMLSlideShow getSlideShow() { - return (XMLSlideShow)getParent().getParent(); - } public XSLFTheme getTheme(){ return getSlideMaster().getTheme(); @@ -143,6 +134,6 @@ public class XSLFSlideLayout extends XSLFSheet { if(_layout.getCSld().isSetBg()) { return new XSLFBackground(_layout.getCSld().getBg(), this); } - return null; + return getSlideMaster().getBackground(); } } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java index 269b76cd1..2ec83609a 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java @@ -19,13 +19,12 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; -import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlException; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; -import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterTextStyles; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; +import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; +import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterTextStyles; +import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; import java.io.IOException; import java.util.HashMap; @@ -123,4 +122,11 @@ import java.util.Map; return props; } + @Override + public XSLFBackground getBackground(){ + if(_slide.getCSld().isSetBg()) { + return new XSLFBackground(_slide.getCSld().getBg(), this); + } + return null; + } } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java index 173d6c856..833e6deb9 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java @@ -27,10 +27,6 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTTable; import org.openxmlformats.schemas.drawingml.x2006.main.CTTableRow; -import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrameNonVisual; @@ -39,7 +35,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.awt.geom.Rectangle2D; /** * Represents a table in a .pptx presentation diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java index cc04a10ae..335e77d59 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java @@ -19,24 +19,21 @@ package org.apache.poi.xslf.usermodel; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCell; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCellProperties; +import org.apache.poi.util.Units; +import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; -import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.STPenAlignment; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCell; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCellProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; import org.openxmlformats.schemas.drawingml.x2006.main.STCompoundLine; import org.openxmlformats.schemas.drawingml.x2006.main.STLineCap; -import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal; -import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties; import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndLength; import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndType; import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndWidth; -import org.apache.poi.util.Internal; -import org.apache.poi.util.Units; +import org.openxmlformats.schemas.drawingml.x2006.main.STPenAlignment; +import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal; import java.awt.*; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableRow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableRow.java index 328c76ae7..c0067e9c7 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableRow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableRow.java @@ -20,16 +20,13 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Units; -import org.apache.xmlbeans.XmlObject; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTable; import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCell; import org.openxmlformats.schemas.drawingml.x2006.main.CTTableRow; -import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.Iterator; +import java.util.List; /** * Represents a table in a .pptx presentation diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java index 154f25e06..4ab67e2de 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java @@ -26,9 +26,9 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyleList; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Collections; @Beta public class XSLFTableStyles extends POIXMLDocumentPart implements Iterable{ diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java index 59428ef4a..177e8099e 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java @@ -19,25 +19,17 @@ package org.apache.poi.xslf.usermodel; -import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.util.Beta; -import org.apache.poi.util.Units; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTShapeNonVisual; import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; -import java.awt.geom.Rectangle2D; - /** * @author Yegor Kozlov diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index c4ac04607..a67dc0d34 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -19,28 +19,27 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; import org.apache.poi.util.Units; -import org.apache.poi.xslf.model.PropertyFetcher; import org.apache.poi.xslf.model.ParagraphPropertyFetcher; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing; import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; +import java.awt.*; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.awt.*; -import java.awt.geom.Rectangle2D; -import java.awt.font.TextLayout; -import java.awt.font.TextAttribute; -import java.awt.font.LineBreakMeasurer; -import java.text.AttributedString; -import java.text.AttributedCharacterIterator; /** * Represents a paragraph of text within the containing text body. @@ -187,7 +186,8 @@ public class XSLFTextParagraph implements Iterable{ ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ public boolean fetch(CTTextParagraphProperties props){ if(props.isSetBuClr()){ - setValue(theme.getColor(props.getBuClr())); + XSLFColor c = new XSLFColor(props.getBuClr(), theme); + setValue(c.getColor()); return true; } return false; @@ -580,7 +580,7 @@ public class XSLFTextParagraph implements Iterable{ string.addAttribute(TextAttribute.FOREGROUND, run.getFontColor(), startIndex, endIndex); string.addAttribute(TextAttribute.FAMILY, run.getFontFamily(), startIndex, endIndex); - string.addAttribute(TextAttribute.SIZE, run.getFontSize(), startIndex, endIndex); + string.addAttribute(TextAttribute.SIZE, (float)run.getFontSize(), startIndex, endIndex); if(run.isBold()) { string.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIndex, endIndex); } @@ -589,10 +589,17 @@ public class XSLFTextParagraph implements Iterable{ } if(run.isUnderline()) { string.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIndex, endIndex); + string.addAttribute(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, startIndex, endIndex); } if(run.isStrikethrough()) { string.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, startIndex, endIndex); } + if(run.isSubscript()) { + string.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, startIndex, endIndex); + } + if(run.isSuperscript()) { + string.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, startIndex, endIndex); + } startIndex = endIndex; } @@ -646,8 +653,8 @@ public class XSLFTextParagraph implements Iterable{ bit.getAttribute(TextAttribute.FOREGROUND) : buColor); str.addAttribute(TextAttribute.FAMILY, buFont); - double fontSize = (Double)bit.getAttribute(TextAttribute.SIZE); - double buSz = getBulletFontSize(); + float fontSize = (Float)bit.getAttribute(TextAttribute.SIZE); + float buSz = (float)getBulletFontSize(); if(buSz > 0) fontSize *= buSz* 0.01; else fontSize = -buSz; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java index 558dbdd45..e4ebf9db5 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java @@ -18,17 +18,14 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Beta; import org.apache.poi.xslf.model.CharacterPropertyFetcher; -import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; -import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; import java.awt.*; @@ -78,7 +75,8 @@ public class XSLFTextRun { public boolean fetch(CTTextCharacterProperties props){ CTSolidColorFillProperties solidFill = props.getSolidFill(); if(solidFill != null){ - setValue(theme.getSolidFillColor(solidFill)); + Color c = new XSLFColor(solidFill, theme).getColor(); + setValue(c); return true; } return false; @@ -202,6 +200,40 @@ public class XSLFTextRun { return fetcher.getValue() == null ? false : fetcher.getValue(); } + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public boolean isSuperscript() { + CharacterPropertyFetcher fetcher = new CharacterPropertyFetcher(_p.getLevel()){ + public boolean fetch(CTTextCharacterProperties props){ + if(props.isSetBaseline()){ + setValue(props.getBaseline() > 0); + return true; + } + return false; + } + }; + fetchCharacterProperty(fetcher); + return fetcher.getValue() == null ? false : fetcher.getValue(); + } + + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public boolean isSubscript() { + CharacterPropertyFetcher fetcher = new CharacterPropertyFetcher(_p.getLevel()){ + public boolean fetch(CTTextCharacterProperties props){ + if(props.isSetBaseline()){ + setValue(props.getBaseline() < 0); + return true; + } + return false; + } + }; + fetchCharacterProperty(fetcher); + return fetcher.getValue() == null ? false : fetcher.getValue(); + } + /** * Specifies whether this run of text will be formatted as bold text * @@ -314,4 +346,4 @@ public class XSLFTextRun { return ok; } -} \ No newline at end of file +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java index c925922d5..279cac919 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java @@ -23,9 +23,6 @@ import org.apache.poi.util.Beta; import org.apache.poi.util.Units; import org.apache.poi.xslf.model.PropertyFetcher; import org.apache.poi.xslf.model.TextBodyPropertyFetcher; -import org.apache.poi.xslf.model.geom.Context; -import org.apache.poi.xslf.model.geom.CustomGeometry; -import org.apache.poi.xslf.model.geom.Path; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties; @@ -36,9 +33,8 @@ import org.openxmlformats.schemas.drawingml.x2006.main.STTextWrappingType; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import java.awt.*; -import java.awt.geom.GeneralPath; -import java.awt.image.BufferedImage; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; @@ -392,19 +388,19 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { // shadow XSLFShadow shadow = getShadow(); - Color fillColor = getFillColor(); - Color lineColor = getLineColor(); + Paint fill = getFill(graphics); + Paint line = getLinePaint(graphics); if(shadow != null) { - //shadow.draw(graphics); + shadow.draw(graphics); } - if (fillColor != null) { - graphics.setColor(fillColor); - applyFill(graphics); + + if(fill != null) { + graphics.setPaint(fill); graphics.fill(outline); } - if (lineColor != null){ - graphics.setColor(lineColor); + if (line != null){ + graphics.setPaint(line); applyStroke(graphics); graphics.draw(outline); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java index a648dbdf7..77dd0a856 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java @@ -24,28 +24,22 @@ import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTBaseStyles; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTColorScheme; -import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetColor; +import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; import javax.xml.namespace.QName; - -import java.awt.Color; import java.io.IOException; import java.io.OutputStream; -import java.util.Map; import java.util.HashMap; +import java.util.Map; @Beta public class XSLFTheme extends POIXMLDocumentPart { private CTOfficeStyleSheet _theme; - private Map _schemeColors; + private Map _schemeColors; XSLFTheme() { super(); @@ -64,12 +58,13 @@ public class XSLFTheme extends POIXMLDocumentPart { CTBaseStyles elems = _theme.getThemeElements(); CTColorScheme scheme = elems.getClrScheme(); // The color scheme is responsible for defining a list of twelve colors. - _schemeColors = new HashMap(12); + _schemeColors = new HashMap(12); for(XmlObject o : scheme.selectPath("*")){ CTColor c = (CTColor)o; String name = c.getDomNode().getLocalName(); - _schemeColors.put(name, new XSLFColor(c)); + _schemeColors.put(name, c); } + _schemeColors.put("bg1", _schemeColors.get("lt1")); _schemeColors.put("bg2", _schemeColors.get("lt2")); _schemeColors.put("tx1", _schemeColors.get("dk1")); @@ -89,141 +84,10 @@ public class XSLFTheme extends POIXMLDocumentPart { * * @return a theme color or null if not found */ - public XSLFColor getColor(String name){ + CTColor getCTColor(String name){ return _schemeColors.get(name); } - Color getSchemeColor(CTSchemeColor schemeColor){ - String colorRef = schemeColor.getVal().toString(); - int alpha = 0xFF; - if(schemeColor.sizeOfAlphaArray() > 0){ - int aval = schemeColor.getAlphaArray(0).getVal(); - alpha = Math.round(255 * aval / 100000f); - } - Color themeColor = _schemeColors.get(colorRef).getColor(alpha); - - int lumMod = 100, lumOff = 0; - if (schemeColor.sizeOfLumModArray() > 0) { - lumMod = schemeColor.getLumModArray(0).getVal() / 1000; - } - if (schemeColor.sizeOfLumOffArray() > 0) { - lumOff = schemeColor.getLumOffArray(0).getVal() / 1000; - } - if(schemeColor.sizeOfShadeArray() > 0) { - lumMod = schemeColor.getShadeArray(0).getVal() / 1000; - } - Color color = modulateLuminanace(themeColor, lumMod, lumOff); - - if(schemeColor.sizeOfTintArray() > 0) { - float tint = schemeColor.getTintArray(0).getVal() / 100000f; - int red = Math.round(tint * themeColor.getRed() + (1 - tint) * 255); - int green = Math.round(tint * themeColor.getGreen() + (1 - tint) * 255); - int blue = Math.round(tint * themeColor.getBlue() + (1 - tint) * 255); - color = new Color(red, green, blue); - } - - return color; - } - - /** - * TODO get rid of code duplication. Re-write to use xpath instead of beans - */ - Color getPresetColor(CTPresetColor presetColor){ - String colorName = presetColor.getVal().toString(); - Color color; - try { - color = (Color)Color.class.getField(colorName).get(null); - } catch (Exception e){ - color = Color.black; - } - if(presetColor.sizeOfAlphaArray() > 0){ - int aval = presetColor.getAlphaArray(0).getVal(); - int alpha = Math.round(255 * aval / 100000f); - color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); - } - - int lumMod = 100, lumOff = 0; - if (presetColor.sizeOfLumModArray() > 0) { - lumMod = presetColor.getLumModArray(0).getVal() / 1000; - } - if (presetColor.sizeOfLumOffArray() > 0) { - lumOff = presetColor.getLumOffArray(0).getVal() / 1000; - } - if(presetColor.sizeOfShadeArray() > 0) { - lumMod = presetColor.getShadeArray(0).getVal() / 1000; - } - color = modulateLuminanace(color, lumMod, lumOff); - - if(presetColor.sizeOfTintArray() > 0) { - float tint = presetColor.getTintArray(0).getVal() / 100000f; - int red = Math.round(tint * color.getRed() + (1 - tint) * 255); - int green = Math.round(tint * color.getGreen() + (1 - tint) * 255); - int blue = Math.round(tint * color.getBlue() + (1 - tint) * 255); - color = new Color(red, green, blue); - } - - return color; - } - - public Color brighter(Color color, double tint) { - int r = color.getRed(); - int g = color.getGreen(); - int b = color.getBlue(); - - /* From 2D group: - * 1. black.brighter() should return grey - * 2. applying brighter to blue will always return blue, brighter - * 3. non pure color (non zero rgb) will eventually return white - */ - int i = (int)(1.0/(1.0-tint)); - if ( r == 0 && g == 0 && b == 0) { - return new Color(i, i, i); - } - if ( r > 0 && r < i ) r = i; - if ( g > 0 && g < i ) g = i; - if ( b > 0 && b < i ) b = i; - - return new Color(Math.min((int)(r/tint), 255), - Math.min((int)(g/tint), 255), - Math.min((int)(b/tint), 255)); - } - - Color getSrgbColor(CTSRgbColor srgb){ - byte[] val = srgb.getVal(); - int alpha = 0xFF; - if(srgb.sizeOfAlphaArray() > 0){ - int aval = srgb.getAlphaArray(0).getVal(); - alpha = Math.round(255 * aval / 100000f); - } - return new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2], alpha); - } - - Color getSolidFillColor(CTSolidColorFillProperties solidFill){ - Color color; - if (solidFill.isSetSrgbClr()) { - color = getSrgbColor(solidFill.getSrgbClr()); - } else if (solidFill.isSetSchemeClr()) { - color = getSchemeColor(solidFill.getSchemeClr()); - } else { - // TODO support other types - color = Color.black; - } - return color; - } - - Color getColor(CTColor solidFill){ - Color color; - if (solidFill.isSetSrgbClr()) { - color = getSrgbColor(solidFill.getSrgbClr()); - } else if (solidFill.isSetSchemeClr()) { - color = getSchemeColor(solidFill.getSchemeClr()); - } else { - // TODO support other types - color = Color.black; - } - return color; - } - /** * While developing only! */ @@ -247,26 +111,6 @@ public class XSLFTheme extends POIXMLDocumentPart { out.close(); } - public static Color modulateLuminanace(Color c, int lumMod, int lumOff) { - Color color; - if (lumOff > 0) { - color = new Color( - (int) (Math.round((255 - c.getRed()) * (100.0 - lumMod) / 100.0 + c.getRed())), - (int) (Math.round((255 - c.getGreen()) * lumOff / 100.0 + c.getGreen())), - (int) (Math.round((255 - c.getBlue()) * lumOff / 100.0 + c.getBlue())), - c.getAlpha() - ); - } else { - color = new Color( - (int) (Math.round(c.getRed() * lumMod / 100.0)), - (int) (Math.round(c.getGreen() * lumMod / 100.0)), - (int) (Math.round(c.getBlue() * lumMod / 100.0)), - c.getAlpha() - ); - } - return color; - } - public String getMajorFont(){ return _theme.getThemeElements().getFontScheme().getMajorFont().getLatin().getTypeface(); } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java new file mode 100644 index 000000000..1f8826fda --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * 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.xslf.usermodel; + +import junit.framework.TestCase; +import org.apache.poi.xslf.XSLFTestDataSamples; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +/** + * Date: 10/26/11 + * + * @author Yegor Kozlov + */ +public class TestPPTX2PNG extends TestCase { + public void testRender(){ + String[] testFiles = {"layouts.pptx", "sample.pptx", "shapes.pptx", + "45541_Header.pptx", "backgrounds.pptx"}; + for(String sampleFile : testFiles){ + XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument(sampleFile); + Dimension pg = pptx.getPageSize(); + for(XSLFSlide slide : pptx.getSlides()){ + BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = img.createGraphics(); + + slide.draw(graphics); + + } + } + } +} diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java new file mode 100755 index 000000000..ffb25b37e --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java @@ -0,0 +1,154 @@ +/* ==================================================================== + 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.xslf.usermodel; + +import junit.framework.TestCase; + +import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; +import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTHslColor; +import org.openxmlformats.schemas.drawingml.x2006.main.STPresetColorVal; + +import java.awt.*; + +/** + * @author Yegor Kozlov + */ +public class TestXSLFColor extends TestCase { + + public void testGetters() { + CTColor xml = CTColor.Factory.newInstance(); + CTSRgbColor c = xml.addNewSrgbClr(); + c.setVal(new byte[]{(byte)0xFF, 0, 0}); + + XSLFColor color = new XSLFColor(xml, null); + + assertEquals(-1, color.getAlpha()); + c.addNewAlpha().setVal(50000); + assertEquals(50, color.getAlpha()); + + assertEquals(-1, color.getAlphaMod()); + c.addNewAlphaMod().setVal(50000); + assertEquals(50, color.getAlphaMod()); + + assertEquals(-1, color.getAlphaOff()); + c.addNewAlphaOff().setVal(50000); + assertEquals(50, color.getAlphaOff()); + + assertEquals(-1, color.getLumMod()); + c.addNewLumMod().setVal(50000); + assertEquals(50, color.getLumMod()); + + assertEquals(-1, color.getLumOff()); + c.addNewLumOff().setVal(50000); + assertEquals(50, color.getLumOff()); + + assertEquals(-1, color.getSat()); + c.addNewSat().setVal(50000); + assertEquals(50, color.getSat()); + + assertEquals(-1, color.getSatMod()); + c.addNewSatMod().setVal(50000); + assertEquals(50, color.getSatMod()); + + assertEquals(-1, color.getSatOff()); + c.addNewSatOff().setVal(50000); + assertEquals(50, color.getSatOff()); + + assertEquals(-1, color.getRed()); + c.addNewRed().setVal(50000); + assertEquals(50, color.getRed()); + + assertEquals(-1, color.getGreen()); + c.addNewGreen().setVal(50000); + assertEquals(50, color.getGreen()); + + assertEquals(-1, color.getBlue()); + c.addNewBlue().setVal(50000); + assertEquals(50, color.getRed()); + + assertEquals(-1, color.getShade()); + c.addNewShade().setVal(50000); + assertEquals(50, color.getShade()); + + assertEquals(-1, color.getTint()); + c.addNewTint().setVal(50000); + assertEquals(50, color.getTint()); + } + + public void testHSL() { + CTColor xml = CTColor.Factory.newInstance(); + CTHslColor c = xml.addNewHslClr(); + c.setHue2(14400000); + c.setSat2(100000); + c.setLum2(50000); + + XSLFColor color = new XSLFColor(xml, null); + assertEquals(new Color(128, 00, 00), color.getColor()); + } + + public void testSRgb() { + CTColor xml = CTColor.Factory.newInstance(); + xml.addNewSrgbClr().setVal(new byte[]{ (byte)0xFF, (byte)0xFF, 0}); + + XSLFColor color = new XSLFColor(xml, null); + assertEquals(new Color(0xFF, 0xFF, 0), color.getColor()); + } + + public void testSchemeColor() { + XMLSlideShow ppt = new XMLSlideShow(); + XSLFTheme theme = ppt.createSlide().getTheme(); + + CTColor xml = CTColor.Factory.newInstance(); + xml.addNewSchemeClr().setVal(STSchemeColorVal.ACCENT_2); + + XSLFColor color = new XSLFColor(xml, theme); + // accent2 is theme1.xml is + assertEquals(Color.decode("0xC0504D"), color.getColor()); + + xml = CTColor.Factory.newInstance(); + xml.addNewSchemeClr().setVal(STSchemeColorVal.LT_1); + color = new XSLFColor(xml, theme); + // + assertEquals(Color.decode("0xFFFFFF"), color.getColor()); + + xml = CTColor.Factory.newInstance(); + xml.addNewSchemeClr().setVal(STSchemeColorVal.DK_1); + color = new XSLFColor(xml, theme); + // + assertEquals(Color.decode("0x000000"), color.getColor()); + } + + public void testPresetColor() { + CTColor xml = CTColor.Factory.newInstance(); + xml.addNewPrstClr().setVal(STPresetColorVal.AQUAMARINE); + XSLFColor color = new XSLFColor(xml, null); + assertEquals(new Color(127, 255, 212), color.getColor()); + + + for(String colorName : XSLFColor.presetColors.keySet()){ + xml = CTColor.Factory.newInstance(); + STPresetColorVal.Enum val = STPresetColorVal.Enum.forString(colorName); + assertNotNull(colorName, val); + xml.addNewPrstClr().setVal(val); + color = new XSLFColor(xml, null); + assertEquals(XSLFColor.presetColors.get(colorName), color.getColor()); + } + } +} \ No newline at end of file diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSimpleShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSimpleShape.java index 53562d967..e4d4d553b 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSimpleShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSimpleShape.java @@ -143,13 +143,13 @@ public class TestXSLFSimpleShape extends TestCase { assertEquals(2.0, s.getLineWidth()); assertEquals(LineCap.FLAT, s.getLineCap()); // YK: calculated color is slightly different from PowerPoint - assertEquals(new Color(40, 65, 95), s.getLineColor()); + assertEquals(new Color(39, 64, 94), s.getLineColor()); } XSLFSimpleShape s0 = (XSLFSimpleShape) shapes[0]; // fill is not set assertNull(s0.getSpPr().getSolidFill()); - assertEquals(slide6.getTheme().getColor("accent1").getColor(), s0.getFillColor()); + //assertEquals(slide6.getTheme().getColor("accent1").getColor(), s0.getFillColor()); assertEquals(new Color(79, 129, 189), s0.getFillColor()); // lighter 80% diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java index 746098be0..81f35d325 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java @@ -33,7 +33,7 @@ public class TestXSLFTheme extends TestCase { assertNotNull(theme); assertEquals("Office Theme", theme.getName()); - XSLFColor accent1 = theme.getColor("accent1"); - assertNotNull(accent1); + //XSLFColor accent1 = theme.getColor("accent1"); + //assertNotNull(accent1); } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java index 89dfcb145..0cf9db5ac 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java @@ -138,7 +138,7 @@ public class TestXWPFPictureData extends TestCase { for (XWPFRun run : paragraph.getRuns()) { for (XWPFPicture picture : run.getEmbeddedPictures()) { if (paragraph.getDocument() != null) { - System.out.println(picture.getCTPicture()); + //System.out.println(picture.getCTPicture()); XWPFPictureData data = picture.getPictureData(); if(data != null) System.out.println(data.getFileName()); } diff --git a/test-data/slideshow/backgrounds.pptx b/test-data/slideshow/backgrounds.pptx new file mode 100755 index 0000000000000000000000000000000000000000..1ce0557fbf6d4dc8d9327bbf9bea47b3089b55a8 GIT binary patch literal 63442 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 z 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\e-j_>P8V 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 literal 0 HcmV?d00001