diff options
6 files changed, 203 insertions, 2 deletions
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java index ea1c179d66..c9f3cfd0c0 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java @@ -30,6 +30,7 @@ import org.apache.poi.ooxml.util.POIXMLUnits; import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.sl.draw.DrawPaint; +import org.apache.poi.sl.usermodel.HighlightColorSupport; import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.TextRun; @@ -40,10 +41,12 @@ import org.apache.poi.xslf.model.CharacterPropertyFetcher; import org.apache.poi.xslf.model.CharacterPropertyFetcher.CharPropFetcher; import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties; import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTFontCollection; import org.openxmlformats.schemas.drawingml.x2006.main.CTFontScheme; import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle; import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; @@ -55,13 +58,14 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; +import org.openxmlformats.schemas.drawingml.x2006.main.impl.CTSRgbColorImpl; /** * Represents a run of text within the containing text body. The run element is the * lowest level text separation mechanism within a text body. */ @Beta -public class XSLFTextRun implements TextRun { +public class XSLFTextRun implements TextRun, HighlightColorSupport { private static final Logger LOG = LogManager.getLogger(XSLFTextRun.class); private final XmlObject _r; @@ -158,6 +162,86 @@ public class XSLFTextRun implements TextRun { } + /** + * Returns the font highlight (background) color for this text run. + * This returns a {@link SolidPaint}, or null if no highlight is set. + * + * @return The font highlight (background) colour associated with the run, null if no highlight. + * + * @see org.apache.poi.sl.draw.DrawPaint#getPaint(java.awt.Graphics2D, PaintStyle) + * @see SolidPaint#getSolidColor() + * @since POI 5.2.4 + */ + @Override + public PaintStyle getHighlightColor() { + XSLFShape shape = getParagraph().getParentShape(); + final boolean hasPlaceholder = shape.getPlaceholder() != null; + return fetchCharacterProperty((props, highlightColor) -> fetchHighlightColor(props, highlightColor, shape, hasPlaceholder)); + } + + + private static void fetchHighlightColor(CTTextCharacterProperties props, Consumer<PaintStyle> highlightColor, XSLFShape shape, boolean hasPlaceholder) { + if (props == null) { + return; + } + + final CTColor col = props.getHighlight(); + if (col == null) { + return; + } + + final CTSRgbColor rgbCol = col.getSrgbClr(); + final byte[] cols = rgbCol.getVal(); + final SolidPaint paint = DrawPaint.createSolidPaint(new Color(0xFF & cols[0], 0xFF & cols[1], 0xFF & cols[2])); + highlightColor.accept(paint); + } + + /** + * Sets the font highlight (background) color for this text run - convenience function + * + * @param color The highlight (background) color to set. + * @since POI 5.2.4 + */ + @Override + public void setHighlightColor(final Color color) { + setHighlightColor(DrawPaint.createSolidPaint(color)); + } + + /** + * Set the highlight (background) color for this text run. + * + * @param color The highlight (background) color to set. + * @throws IllegalArgumentException If the supplied paint style is not null or a SolidPaint. + * + * @see org.apache.poi.sl.draw.DrawPaint#createSolidPaint(Color) + * @since POI 5.2.4 + */ + @Override + public void setHighlightColor(final PaintStyle color) { + if (color == null) { + final CTTextCharacterProperties rPr = getRPr(true); + if (rPr.isSetHighlight()) { + rPr.unsetHighlight(); + } + return; + } + + if (!(color instanceof SolidPaint)) { + throw new IllegalArgumentException("Currently only SolidPaint is supported!"); + } + + final SolidPaint sp = (SolidPaint)color; + final Color c = DrawPaint.applyColorTransform(sp.getSolidColor()); + + final CTTextCharacterProperties rPr = getRPr(true); + final CTColor highlight = rPr.isSetHighlight() ? rPr.getHighlight() : rPr.addNewHighlight(); + + final CTSRgbColor col = CTSRgbColor.Factory.newInstance(); + col.setVal(new byte[] {(byte)c.getRed(), (byte)c.getGreen(), (byte)c.getBlue()}); + + highlight.setSrgbClr(col); + } + @Override public void setFontSize(Double fontSize){ diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java index d80818d1fe..5d73bdf1a4 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java @@ -19,6 +19,7 @@ package org.apache.poi.xslf.usermodel; import static org.apache.poi.sl.usermodel.BaseTestSlideShow.getColor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -26,16 +27,22 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.awt.Color; import java.awt.Graphics2D; +import java.awt.font.TextAttribute; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; import java.util.List; +import java.util.Map; +import org.apache.commons.io.output.NullPrintStream; import org.apache.poi.sl.draw.DrawTextFragment; import org.apache.poi.sl.draw.DrawTextParagraph; import org.apache.poi.sl.usermodel.AutoNumberingScheme; import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; import org.apache.poi.xslf.XSLFTestDataSamples; +import org.apache.poi.xslf.util.DummyGraphics2d; import org.junit.jupiter.api.Test; class TestXSLFTextParagraph { @@ -427,4 +434,65 @@ class TestXSLFTextParagraph { assertThrows(IllegalStateException.class, () -> r2.setText("aaa")); } } + + @Test + void testHighlightRender() throws IOException { + try (XMLSlideShow ppt = new XMLSlideShow()) { + XSLFSlide slide = ppt.createSlide(); + XSLFTextShape sh = slide.createAutoShape(); + + XSLFTextParagraph p = sh.addNewTextParagraph(); + XSLFTextRun r1 = p.addNewTextRun(); + r1.setText("This is a "); + XSLFTextRun r2 = p.addNewTextRun(); + r2.setText("highlight"); + r2.setHighlightColor(Color.yellow); + XSLFTextRun r3 = p.addNewTextRun(); + r3.setText(" test"); + + assertEquals("This is a highlight test", sh.getText()); + + DummyGraphics2d dgfx = new DummyGraphics2d(new NullPrintStream()) { + @Override + public void drawString(AttributedCharacterIterator iterator, float x, float y) { + // For the test file, common sl draws textruns one by one and not mixed + // so we evaluate the whole iterator + Map<AttributedCharacterIterator.Attribute, Object> attributes = null; + StringBuilder sb = new StringBuilder(); + + for (char c = iterator.first(); + c != CharacterIterator.DONE; + c = iterator.next()) { + sb.append(c); + attributes = iterator.getAttributes(); + } + + if ("This is a".equals(sb.toString())) { + // Should be no background. + assertNotNull(attributes); + Object background = attributes.get(TextAttribute.BACKGROUND); + assertNull(background); + } + if ("highlight".equals(sb.toString())) { + // Should be yellow background. + assertNotNull(attributes); + Object background = attributes.get(TextAttribute.BACKGROUND); + assertNotNull(background); + assertTrue(background instanceof Color); + assertEquals(Color.yellow, background); + } + if (" test".equals(sb.toString())) { + // Should be no background. + assertNotNull(attributes); + Object background = attributes.get(TextAttribute.BACKGROUND); + assertNull(background); + } + + } + }; + + ppt.getSlides().get(0).draw(dgfx); + } + } + } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextRun.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextRun.java index 7e92cea273..944880c121 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextRun.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextRun.java @@ -23,15 +23,26 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.awt.Color; import java.io.IOException; import java.io.InputStream; import org.apache.poi.POIDataSamples; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.draw.DrawTextParagraph; +import org.apache.poi.sl.usermodel.PaintStyle; +import org.apache.poi.xslf.model.PropertyFetcher; import org.junit.jupiter.api.Test; +import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle; +import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; @@ -80,6 +91,32 @@ class TestXSLFTextRun { r.setSubscript(false); assertFalse(r.isSubscript()); + // Test highlight get/set. + assertNull(r.getHighlightColor()); + r.setHighlightColor(Color.yellow); + assertEquals(DrawPaint.createSolidPaint(Color.yellow), r.getHighlightColor()); + r.setHighlightColor(DrawPaint.createSolidPaint(Color.blue)); + assertEquals(DrawPaint.createSolidPaint(Color.blue), r.getHighlightColor()); + + r.setHighlightColor((Color) null); + assertNull(r.getHighlightColor()); + r.setHighlightColor((PaintStyle) null); + assertNull(r.getHighlightColor()); + + try { + // make a dummy gradient to test paint rejection. + final XSLFSheet sheet = sh.getSheet(); + final XSLFTheme theme = sheet.getTheme(); + final CTSchemeColor phClr = CTSchemeColor.Factory.newInstance(); + final CTGradientFillProperties gradFill = CTGradientFillProperties.Factory.newInstance(); + final XSLFGradientPaint dummyGrad = new XSLFGradientPaint(gradFill, phClr, theme, sheet); + + r.setHighlightColor(dummyGrad); + fail("Expected non solid paint to cause an exception."); + } catch (IllegalArgumentException iae) { + // Expected, do nothing. + } + ppt.close(); } diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hslf/usermodel/TestTextRun.java b/poi-scratchpad/src/test/java/org/apache/poi/hslf/usermodel/TestTextRun.java index f325f2cf92..a361ad650b 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hslf/usermodel/TestTextRun.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hslf/usermodel/TestTextRun.java @@ -545,6 +545,7 @@ public final class TestTextRun { } } + @Test void testAppendEmpty() throws IOException { try (HSLFSlideShow ppt = new HSLFSlideShow()) { diff --git a/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java index b0cd9598de..a07b15efce 100644 --- a/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -47,6 +47,7 @@ import org.apache.poi.common.usermodel.fonts.FontGroup; import org.apache.poi.common.usermodel.fonts.FontGroup.FontGroupRange; import org.apache.poi.common.usermodel.fonts.FontInfo; import org.apache.poi.sl.usermodel.AutoNumberingScheme; +import org.apache.poi.sl.usermodel.HighlightColorSupport; import org.apache.poi.sl.usermodel.Hyperlink; import org.apache.poi.sl.usermodel.Insets2D; import org.apache.poi.sl.usermodel.PaintStyle; @@ -65,6 +66,7 @@ import org.apache.poi.util.Internal; import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.StringUtil; import org.apache.poi.util.Units; +import org.w3c.dom.Text; public class DrawTextParagraph implements Drawable { private static final Logger LOG = LogManager.getLogger(DrawTextParagraph.class); @@ -610,6 +612,15 @@ public class DrawTextParagraph implements Drawable { att.put(TextAttribute.FOREGROUND, fgPaint); + if (run instanceof HighlightColorSupport) { + // Highlight color is only supported in XSLF (PPTX) text runs. + final PaintStyle highlightPaintStyle = ((HighlightColorSupport)run).getHighlightColor(); + if (highlightPaintStyle != null) { + final Paint bgPaint = dp.getPaint(graphics, highlightPaintStyle); + att.put(TextAttribute.BACKGROUND, bgPaint); + } + } + Double fontSz = run.getFontSize(); if (fontSz == null) { fontSz = paragraph.getDefaultFontSize(); diff --git a/poi/src/main/java/org/apache/poi/sl/usermodel/TextRun.java b/poi/src/main/java/org/apache/poi/sl/usermodel/TextRun.java index 11bc79c8ec..85a7679c30 100644 --- a/poi/src/main/java/org/apache/poi/sl/usermodel/TextRun.java +++ b/poi/src/main/java/org/apache/poi/sl/usermodel/TextRun.java @@ -29,6 +29,7 @@ import org.apache.poi.util.Internal; */ @SuppressWarnings({"unused","java:S1452"}) public interface TextRun { + /** * Type of text capitals */ @@ -78,7 +79,6 @@ public interface TextRun { */ void setFontColor(PaintStyle color); - /** * Returns the font size which is either set directly on this text run or * given from the slide layout |