diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java index 2bebffc20..25dbe68d7 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPaint.java +++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java @@ -384,7 +384,7 @@ public class DrawPaint { * * @returns the RGB Color object */ - private static Color HSL2RGB(double h, double s, double l, double alpha) { + public static Color HSL2RGB(double h, double s, double l, double alpha) { // we clamp the values, as it possible to come up with more than 100% sat/lum // (see links in applyColorTransform() for more info) s = Math.max(0, Math.min(100, s)); @@ -491,5 +491,34 @@ public class DrawPaint { return new double[] {h, s * 100, l * 100}; } - + + /** + * Convert sRGB float component [0..1] from sRGB to linear RGB [0..100000] + * + * @see Color#getRGBColorComponents(float[]) + */ + public static int srgb2lin(float sRGB) { + // scRGB has a linear gamma of 1.0, scale the AWT-Color which is in sRGB to linear RGB + // see https://en.wikipedia.org/wiki/SRGB (the reverse transformation) + if (sRGB <= 0.04045d) { + return (int)Math.rint(100000d * sRGB / 12.92d); + } else { + return (int)Math.rint(100000d * Math.pow((sRGB + 0.055d) / 1.055d, 2.4d)); + } + } + + /** + * Convert linear RGB [0..100000] to sRGB float component [0..1] + * + * @see Color#getRGBColorComponents(float[]) + */ + public static float lin2srgb(int linRGB) { + // color in percentage is in linear RGB color space, i.e. needs to be gamma corrected for AWT color + // see https://en.wikipedia.org/wiki/SRGB (The forward transformation) + if (linRGB <= 0.0031308d) { + return (float)(linRGB / 100000d * 12.92d); + } else { + return (float)(1.055d * Math.pow(linRGB / 100000d, 1.0d/2.4d) - 0.055d); + } + } } \ No newline at end of file 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 f2e94faea..e9a889d76 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java @@ -30,6 +30,7 @@ import org.apache.poi.util.POILogger; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTHslColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveFixedPercentage; import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTScRgbColor; @@ -122,9 +123,7 @@ public class XSLFColor { int h = hsl.getHue2(); int s = hsl.getSat2(); int l = hsl.getLum2(); - // This conversion is not correct and differs from PowerPoint. - // TODO: Revisit and improve. - color = Color.getHSBColor(h / 60000f, s / 100000f, l / 100000f); + color = DrawPaint.HSL2RGB(h / 60000d, s / 1000d, l / 1000d, 1d); } else if (ch instanceof CTPresetColor) { CTPresetColor prst = (CTPresetColor)ch; String colorName = prst.getVal().toString(); @@ -143,13 +142,11 @@ public class XSLFColor { CTColor ctColor = theme.getCTColor(colorRef); if(ctColor != null) color = toColor(ctColor, null); } else if (ch instanceof CTScRgbColor) { - // same as CTSRgbColor but with values expressed in percents + // color in percentage is in linear RGB color space, i.e. needs to be gamma corrected for AWT color 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); + color = new Color(DrawPaint.lin2srgb(scrgb.getR()), DrawPaint.lin2srgb(scrgb.getG()), DrawPaint.lin2srgb(scrgb.getB())); } else if (ch instanceof CTSRgbColor) { + // color in sRGB color space, i.e. same as AWT Color CTSRgbColor srgb = (CTSRgbColor)ch; byte[] val = srgb.getVal(); color = new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2]); @@ -190,24 +187,11 @@ public class XSLFColor { if (fill.isSetSrgbClr()) { fill.unsetSrgbClr(); } - - CTSRgbColor rgb = fill.addNewSrgbClr(); - - float[] rgbaf = color.getRGBComponents(null); - int r = color.getRed(), g = color.getGreen(), b = color.getBlue(); - if (rgbaf[0]*255f == r && rgbaf[1]*255f == g && rgbaf[2]*255f == b) { - rgb.setVal(new byte[]{(byte)r, (byte)g, (byte)b }); - } else { - rgb.addNewRed().setVal((int)(100000 * rgbaf[0])); - rgb.addNewGreen().setVal((int)(100000 * rgbaf[1])); - rgb.addNewBlue().setVal((int)(100000 * rgbaf[2])); - } - // alpha (%) - if (rgbaf.length == 4 && rgbaf[3] < 1f) { - rgb.addNewAlpha().setVal((int)(100000 * rgbaf[3])); + if (fill.isSetScrgbClr()) { + fill.unsetScrgbClr(); } - + if (fill.isSetHslClr()) { fill.unsetHslClr(); } @@ -220,13 +204,41 @@ public class XSLFColor { fill.unsetSchemeClr(); } - if (fill.isSetScrgbClr()) { - fill.unsetScrgbClr(); - } - if (fill.isSetSysClr()) { fill.unsetSysClr(); } + + float[] rgbaf = color.getRGBComponents(null); + boolean addAlpha = (rgbaf.length == 4 && rgbaf[3] < 1f); + CTPositiveFixedPercentage alphaPct; + + // see office open xml part 4 - 5.1.2.2.30 and 5.1.2.2.32 + if (isInt(rgbaf[0]) && isInt(rgbaf[1]) && isInt(rgbaf[2])) { + // sRGB has a gamma of 2.2 + CTSRgbColor rgb = fill.addNewSrgbClr(); + + byte rgbBytes[] = { (byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue() }; + rgb.setVal(rgbBytes); + alphaPct = (addAlpha) ? rgb.addNewAlpha() : null; + } else { + CTScRgbColor rgb = fill.addNewScrgbClr(); + rgb.setR(DrawPaint.srgb2lin(rgbaf[0])); + rgb.setG(DrawPaint.srgb2lin(rgbaf[1])); + rgb.setB(DrawPaint.srgb2lin(rgbaf[2])); + alphaPct = (addAlpha) ? rgb.addNewAlpha() : null; + } + + // alpha (%) + if (alphaPct != null) { + alphaPct.setVal((int)(100000 * rgbaf[3])); + } + } + + /** + * @return true, if this is an integer color value + */ + private static boolean isInt(float f) { + return Math.abs((f*255f) - Math.rint(f*255f)) < 0.00001f; } private int getRawValue(String elem) { diff --git a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java index 7c55ce7fb..b0c0e5243 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java @@ -40,10 +40,13 @@ import javax.imageio.ImageIO; import org.apache.poi.POIDataSamples; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLDocumentPart.RelationPart; +import org.apache.poi.sl.draw.DrawPaint; +import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.PictureData; import org.apache.poi.sl.usermodel.PictureData.PictureType; import org.apache.poi.sl.usermodel.ShapeType; +import org.apache.poi.sl.usermodel.VerticalAlignment; import org.apache.poi.xslf.usermodel.DrawingParagraph; import org.apache.poi.xslf.usermodel.DrawingTextBody; import org.apache.poi.xslf.usermodel.XMLSlideShow; @@ -56,6 +59,7 @@ import org.apache.poi.xslf.usermodel.XSLFShape; import org.apache.poi.xslf.usermodel.XSLFSlide; import org.apache.poi.xslf.usermodel.XSLFSlideLayout; import org.apache.poi.xslf.usermodel.XSLFSlideMaster; +import org.apache.poi.xslf.usermodel.XSLFTextRun; import org.junit.Ignore; import org.junit.Test; @@ -478,25 +482,39 @@ public class TestXSLFBugs { @Test public void bug58217() throws IOException { + Color fillColor = new Color(1f,1f,0f,0.1f); + Color lineColor = new Color(25.3f/255f,1f,0f,0.4f); + Color textColor = new Color(1f,1f,0f,0.6f); + XMLSlideShow ppt1 = new XMLSlideShow(); XSLFSlide sl = ppt1.createSlide(); XSLFAutoShape as = sl.createAutoShape(); as.setShapeType(ShapeType.STAR_10); as.setAnchor(new Rectangle2D.Double(100,100,300,300)); - as.setFillColor(new Color(1f,1f,0f,0.1f)); - as.setLineColor(new Color(1f,1f,0f,0.4f)); + as.setFillColor(fillColor); + as.setLineColor(lineColor); as.setText("Alpha"); - as.getTextParagraphs().get(0).getTextRuns().get(0).setFontColor(new Color(1f,1f,0f,0.6f)); + as.setVerticalAlignment(VerticalAlignment.MIDDLE); + as.setHorizontalCentered(true); + XSLFTextRun tr = as.getTextParagraphs().get(0).getTextRuns().get(0); + tr.setFontSize(32d); + tr.setFontColor(textColor); XMLSlideShow ppt2 = XSLFTestDataSamples.writeOutAndReadBack(ppt1); ppt1.close(); sl = ppt2.getSlides().get(0); as = (XSLFAutoShape)sl.getShapes().get(0); - SolidPaint ps = (SolidPaint)as.getFillStyle().getPaint(); - assertEquals(10000, ps.getSolidColor().getAlpha()); - ps = (SolidPaint)as.getStrokeStyle().getPaint(); - assertEquals(40000, ps.getSolidColor().getAlpha()); - ps = (SolidPaint)as.getTextParagraphs().get(0).getTextRuns().get(0).getFontColor(); - assertEquals(60000, ps.getSolidColor().getAlpha()); + checkColor(fillColor, as.getFillStyle().getPaint()); + checkColor(lineColor, as.getStrokeStyle().getPaint()); + checkColor(textColor, as.getTextParagraphs().get(0).getTextRuns().get(0).getFontColor()); ppt2.close(); } + + private static void checkColor(Color expected, PaintStyle actualStyle) { + assertTrue(actualStyle instanceof SolidPaint); + SolidPaint ps = (SolidPaint)actualStyle; + Color actual = DrawPaint.applyColorTransform(ps.getSolidColor()); + float expRGB[] = expected.getRGBComponents(null); + float actRGB[] = actual.getRGBComponents(null); + assertArrayEquals(expRGB, actRGB, 0.0001f); + } } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java index 8a923c29d..659f4beae 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java @@ -104,7 +104,7 @@ public class TestXSLFColor { c.setLum2(50000); XSLFColor color = new XSLFColor(xml, null, null); - assertEquals(new Color(128, 00, 00), color.getColor()); + assertEquals(Color.BLUE, color.getColor()); } @Test