diff --git a/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java b/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java index c27e8e4e8..ce6bf5db0 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java +++ b/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java @@ -74,7 +74,7 @@ public final class ApacheconEU08 { slide.addShape(box2); HSLFTextBox box3 = new HSLFTextBox(); - box3.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(32); + box3.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(32d); box3.setText( "Yegor Kozlov\r" + "yegor - apache - org"); @@ -129,10 +129,10 @@ public final class ApacheconEU08 { List tp = box2.getTextParagraphs(); for (int i : new byte[]{0,1,2,8}) { - tp.get(i).getTextRuns().get(0).setFontSize(28); + tp.get(i).getTextRuns().get(0).setFontSize(28d); } for (int i : new byte[]{3,4,5,6,7}) { - tp.get(i).getTextRuns().get(0).setFontSize(24); + tp.get(i).getTextRuns().get(0).setFontSize(24d); tp.get(i).setIndentLevel(1); } box2.setAnchor(new Rectangle(36, 80, 648, 400)); @@ -152,15 +152,15 @@ public final class ApacheconEU08 { HSLFTableCell cell = table1.getCell(i, j); cell.setText(txt1[i][j]); HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0); - rt.setFontSize(10); + rt.setFontSize(10d); rt.setFontFamily("Arial"); rt.setBold(true); if(i == 0){ - rt.setFontSize(32); + rt.setFontSize(32d); rt.setFontColor(Color.white); cell.getFill().setForegroundColor(new Color(0, 153, 204)); } else { - rt.setFontSize(28); + rt.setFontSize(28d); cell.getFill().setForegroundColor(new Color(235, 239, 241)); } cell.setVerticalAlignment(VerticalAlignment.MIDDLE); @@ -186,7 +186,7 @@ public final class ApacheconEU08 { HSLFTextBox box1 = new HSLFTextBox(); box1.setHorizontalCentered(true); - box1.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(24); + box1.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(24d); box1.setText("The source code is available at\r" + "http://people.apache.org/~yegor/apachecon_eu08/"); box1.setAnchor(new Rectangle(80, 356, 553, 65)); @@ -225,7 +225,7 @@ public final class ApacheconEU08 { slide.addShape(box1); HSLFTextBox box2 = new HSLFTextBox(); - box2.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(18); + box2.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(18d); box2.setText("Creating a simple presentation from scratch"); box2.setAnchor(new Rectangle(170, 100, 364, 30)); slide.addShape(box2); @@ -233,7 +233,7 @@ public final class ApacheconEU08 { HSLFTextBox box3 = new HSLFTextBox(); HSLFTextRun rt3 = box3.getTextParagraphs().get(0).getTextRuns().get(0); rt3.setFontFamily("Courier New"); - rt3.setFontSize(8); + rt3.setFontSize(8d); box3.setText( "SlideShow ppt = new SlideShow();\u000b" + "Slide slide = ppt.createSlide();\u000b" + @@ -334,7 +334,7 @@ public final class ApacheconEU08 { slide.addShape(box1); HSLFTextBox box2 = new HSLFTextBox(); - box2.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(18); + box2.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(18d); box2.setText("PPGraphics2D: PowerPoint Graphics2D driver"); box2.setAnchor(new Rectangle(178, 70, 387, 30)); slide.addShape(box2); @@ -342,7 +342,7 @@ public final class ApacheconEU08 { HSLFTextBox box3 = new HSLFTextBox(); HSLFTextRun rt3 = box3.getTextParagraphs().get(0).getTextRuns().get(0); rt3.setFontFamily("Courier New"); - rt3.setFontSize(8); + rt3.setFontSize(8d); box3.setText( "//bar chart data. The first value is the bar color, the second is the width\u000b" + "Object[] def = new Object[]{\u000b" + @@ -446,10 +446,10 @@ public final class ApacheconEU08 { List tp = box2.getTextParagraphs(); for (int i : new byte[]{0,1,3}) { - tp.get(i).getTextRuns().get(0).setFontSize(28); + tp.get(i).getTextRuns().get(0).setFontSize(28d); } for (int i : new byte[]{2,4,5}) { - tp.get(i).getTextRuns().get(0).setFontSize(24); + tp.get(i).getTextRuns().get(0).setFontSize(24d); tp.get(i).setIndentLevel(1); } diff --git a/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java b/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java index 0032bc809..a047d8b80 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java +++ b/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java @@ -37,7 +37,7 @@ public final class BulletsDemo { HSLFTextBox shape = new HSLFTextBox(); HSLFTextParagraph rt = shape.getTextParagraphs().get(0); - rt.getTextRuns().get(0).setFontSize(42); + rt.getTextRuns().get(0).setFontSize(42d); rt.setBullet(true); rt.setIndent(0d); //bullet offset rt.setLeftMargin(50d); //text offset (should be greater than bullet offset) diff --git a/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java b/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java index b931ba1b5..93463aaa9 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java +++ b/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java @@ -55,7 +55,7 @@ public final class TableDemo { HSLFTableCell cell = table1.getCell(i, j); HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0); rt.setFontFamily("Arial"); - rt.setFontSize(10); + rt.setFontSize(10d); if(i == 0){ cell.getFill().setForegroundColor(new Color(227, 227, 227)); } else { @@ -93,17 +93,17 @@ public final class TableDemo { for (int j = 0; j < txt2[i].length; j++) { HSLFTableCell cell = table2.getCell(i, j); HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0); - rt.setFontSize(10); + rt.setFontSize(10d); rt.setFontFamily("Arial"); if(i == 0){ cell.getFill().setForegroundColor(new Color(0, 51, 102)); rt.setFontColor(Color.white); rt.setBold(true); - rt.setFontSize(14); + rt.setFontSize(14d); cell.setHorizontalCentered(true); } else { rt.getTextParagraph().setBullet(true); - rt.setFontSize(12); + rt.setFontSize(12d); rt.getTextParagraph().setAlignment(TextAlign.LEFT); cell.setHorizontalCentered(false); } diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java index 91366b9d0..dcd0dea77 100644 --- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java +++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java @@ -42,7 +42,7 @@ public class Tutorial2 { XSLFTextParagraph p1 = shape1.addNewTextParagraph(); XSLFTextRun r1 = p1.addNewTextRun(); r1.setText("Paragraph Formatting"); - r1.setFontSize(24); + r1.setFontSize(24d); r1.setFontColor(new Color(85, 142, 213)); XSLFTextParagraph p2 = shape1.addNewTextParagraph(); @@ -52,20 +52,20 @@ public class Tutorial2 { p2.setSpaceAfter(300d); // 3 lines after the paragraph XSLFTextRun r2 = p2.addNewTextRun(); r2.setText("Paragraph properties apply to all text residing within the corresponding paragraph."); - r2.setFontSize(16); + r2.setFontSize(16d); XSLFTextParagraph p3 = shape1.addNewTextParagraph(); XSLFTextRun r3 = p3.addNewTextRun(); r3.setText("Run Formatting"); - r3.setFontSize(24); + r3.setFontSize(24d); r3.setFontColor(new Color(85, 142, 213)); XSLFTextParagraph p4 = shape1.addNewTextParagraph(); p4.setSpaceBefore(-20d); // 20 pt from the previous paragraph p4.setSpaceAfter(300d); // 3 lines after the paragraph XSLFTextRun r4 = p4.addNewTextRun(); - r4.setFontSize(16); + r4.setFontSize(16d); r4.setText( "Run level formatting is the most granular property level and allows " + "for the specifying of all low level text properties. The text run is " + 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 ec2d35881..6a9637b2c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -142,6 +142,12 @@ public abstract class XSLFShape implements Shape { if (pr == null) { pr = shape.getGrpSpPr(); } + if (pr == null) { + if (shape.getXmlObject() instanceof CTBackground) { + pr = shape.getXmlObject(); + } + } + if (pr == null) { setValue(PaintStyle.TRANSPARENT_PAINT); return true; @@ -174,29 +180,7 @@ public abstract class XSLFShape implements Shape { if (fillRef == null) { fillRef = getBgRef(); } - if (fillRef == null) { - return PaintStyle.TRANSPARENT_PAINT; - } - - // The idx attribute refers to the index of a fill style or - // background fill style within the presentation's style matrix, defined by the fmtScheme element. - // value of 0 or 1000 indicates no background, - // values 1-999 refer to the index of a fill style within the fillStyleLst element - // values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element. - int idx = (int)fillRef.getIdx(); - CTSchemeColor phClr = fillRef.getSchemeClr(); - XSLFSheet sheet = getSheet(); - XSLFTheme theme = sheet.getTheme(); - XmlObject fillProps = null; - CTStyleMatrix matrix = theme.getXmlObject().getThemeElements().getFmtScheme(); - if(idx >= 1 && idx <= 999){ - fillProps = matrix.getFillStyleLst().selectPath("*")[idx - 1]; - } else if (idx >= 1001 ){ - fillProps = matrix.getBgFillStyleLst().selectPath("*")[idx - 1001]; - } - if(fillProps != null) { - paint = selectPaint(fillProps, phClr, sheet.getPackagePart()); - } + paint = selectPaint(fillRef); return paint == null ? PaintStyle.TRANSPARENT_PAINT : paint; } @@ -250,6 +234,28 @@ public abstract class XSLFShape implements Shape { } return _ph; } + + /** + * Specifies that the corresponding shape should be represented by the generating application + * as a placeholder. When a shape is considered a placeholder by the generating application + * it can have special properties to alert the user that they may enter content into the shape. + * Different types of placeholders are allowed and can be specified by using the placeholder + * type attribute for this element + * + * @param placeholder + */ + protected void setPlaceholder(Placeholder placeholder) { + String xquery = "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:nvPr"; + CTApplicationNonVisualDrawingProps nv = selectProperty(CTApplicationNonVisualDrawingProps.class, xquery); + if (nv == null) return; + if(placeholder == null) { + if (nv.isSetPh()) nv.unsetPh(); + _ph = null; + } else { + nv.addNewPh().setType(STPlaceholderType.Enum.forInt(placeholder.ordinal() + 1)); + } + } + /** * As there's no xmlbeans hierarchy, but XSLF works with subclassing, not all @@ -365,6 +371,8 @@ public abstract class XSLFShape implements Shape { return selectPaint((CTBlipFillProperties)obj, phClr, parentPart); } else if (obj instanceof CTGradientFillProperties) { return selectPaint((CTGradientFillProperties) obj, phClr, parentPart); + } else if (obj instanceof CTStyleMatrixReference) { + return selectPaint((CTStyleMatrixReference)obj); } else { return null; } @@ -479,5 +487,25 @@ public abstract class XSLFShape implements Shape { }; } - + protected PaintStyle selectPaint(CTStyleMatrixReference fillRef) { + if (fillRef == null) return null; + + // The idx attribute refers to the index of a fill style or + // background fill style within the presentation's style matrix, defined by the fmtScheme element. + // value of 0 or 1000 indicates no background, + // values 1-999 refer to the index of a fill style within the fillStyleLst element + // values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element. + int idx = (int)fillRef.getIdx(); + CTSchemeColor phClr = fillRef.getSchemeClr(); + XSLFSheet sheet = getSheet(); + XSLFTheme theme = sheet.getTheme(); + XmlObject fillProps = null; + CTStyleMatrix matrix = theme.getXmlObject().getThemeElements().getFmtScheme(); + if (idx >= 1 && idx <= 999) { + fillProps = matrix.getFillStyleLst().selectPath("*")[idx - 1]; + } else if (idx >= 1001 ){ + fillProps = matrix.getBgFillStyleLst().selectPath("*")[idx - 1001]; + } + return (fillProps == null) ? null : selectPaint(fillProps, phClr, theme.getPackagePart()); + } } \ No newline at end of file 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 fcd28a8b1..938115278 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java @@ -118,7 +118,7 @@ public class XSLFTableCell extends XSLFTextShape { private double getBorderWidth(char bltr) { CTLineProperties ln = getCTLine(bltr, false); - return (ln == null) ? defaultBorderWidth : Units.toPoints(ln.getW()); + return (ln == null || !ln.isSetW()) ? defaultBorderWidth : Units.toPoints(ln.getW()); } private void setBorderColor(char bltr, Color color) { 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 b83849011..994a6034f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -23,6 +23,7 @@ import org.apache.poi.sl.usermodel.AutoNumberingScheme; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.util.*; import org.apache.poi.xslf.model.ParagraphPropertyFetcher; +import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.*; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; @@ -379,9 +380,9 @@ public class XSLFTextParagraph implements TextParagraph { @Override public void setIndent(Double indent){ - if ((indent == null || indent == -1d) && !_p.isSetPPr()) return; + if ((indent == null) && !_p.isSetPPr()) return; CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - if(indent == -1) { + if(indent == null) { if(pr.isSetIndent()) pr.unsetIndent(); } else { pr.setIndent(Units.toEMU(indent)); @@ -441,7 +442,7 @@ public class XSLFTextParagraph implements TextParagraph { public void setRightMargin(Double rightMargin){ if (rightMargin == null && !_p.isSetPPr()) return; CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - if(rightMargin == -1) { + if(rightMargin == null) { if(pr.isSetMarR()) pr.unsetMarR(); } else { pr.setMarR(Units.toEMU(rightMargin)); @@ -788,69 +789,102 @@ public class XSLFTextParagraph implements TextParagraph { return ok; } - void copy(XSLFTextParagraph p){ - TextAlign srcAlign = p.getTextAlign(); + void copy(XSLFTextParagraph other){ + if (other == this) return; + + CTTextParagraph thisP = getXmlObject(); + CTTextParagraph otherP = other.getXmlObject(); + + if (thisP.isSetPPr()) thisP.unsetPPr(); + if (thisP.isSetEndParaRPr()) thisP.unsetEndParaRPr(); + + _runs.clear(); + for (int i=thisP.sizeOfBrArray(); i>0; i--) { + thisP.removeBr(i-1); + } + for (int i=thisP.sizeOfRArray(); i>0; i--) { + thisP.removeR(i-1); + } + for (int i=thisP.sizeOfFldArray(); i>0; i--) { + thisP.removeFld(i-1); + } + + XmlCursor thisC = thisP.newCursor(); + thisC.toEndToken(); + XmlCursor otherC = otherP.newCursor(); + otherC.copyXmlContents(thisC); + otherC.dispose(); + thisC.dispose(); + + List otherRs = other.getTextRuns(); + int i=0; + for(CTRegularTextRun rtr : thisP.getRArray()) { + XSLFTextRun run = new XSLFTextRun(rtr, this); + run.copy(otherRs.get(i++)); + _runs.add(run); + } + + + // set properties again, in case we are based on a different + // template + TextAlign srcAlign = other.getTextAlign(); if(srcAlign != getTextAlign()){ setTextAlign(srcAlign); } - boolean isBullet = p.isBullet(); + boolean isBullet = other.isBullet(); if(isBullet != isBullet()){ setBullet(isBullet); if(isBullet) { - String buFont = p.getBulletFont(); + String buFont = other.getBulletFont(); if(buFont != null && !buFont.equals(getBulletFont())){ setBulletFont(buFont); } - String buChar = p.getBulletCharacter(); + String buChar = other.getBulletCharacter(); if(buChar != null && !buChar.equals(getBulletCharacter())){ setBulletCharacter(buChar); } - Color buColor = p.getBulletFontColor(); + Color buColor = other.getBulletFontColor(); if(buColor != null && !buColor.equals(getBulletFontColor())){ setBulletFontColor(buColor); } - double buSize = p.getBulletFontSize(); - if(buSize != getBulletFontSize()){ + Double buSize = other.getBulletFontSize(); + if(!doubleEquals(buSize, getBulletFontSize())){ setBulletFontSize(buSize); } } } - Double leftMargin = p.getLeftMargin(); - if(leftMargin != getLeftMargin()){ + Double leftMargin = other.getLeftMargin(); + if (!doubleEquals(leftMargin, getLeftMargin())){ setLeftMargin(leftMargin); } - Double indent = p.getIndent(); - if(indent != getIndent()){ + Double indent = other.getIndent(); + if (!doubleEquals(indent, getIndent())) { setIndent(indent); } - Double spaceAfter = p.getSpaceAfter(); - if(spaceAfter != getSpaceAfter()){ + Double spaceAfter = other.getSpaceAfter(); + if (!doubleEquals(spaceAfter, getSpaceAfter())) { setSpaceAfter(spaceAfter); } - Double spaceBefore = p.getSpaceBefore(); - if(spaceBefore != getSpaceBefore()){ + Double spaceBefore = other.getSpaceBefore(); + if (!doubleEquals(spaceBefore, getSpaceBefore())) { setSpaceBefore(spaceBefore); } - Double lineSpacing = p.getLineSpacing(); - if(lineSpacing != getLineSpacing()){ + Double lineSpacing = other.getLineSpacing(); + if (!doubleEquals(lineSpacing, getLineSpacing())) { setLineSpacing(lineSpacing); } - - List srcR = p.getTextRuns(); - List tgtR = getTextRuns(); - for(int i = 0; i < srcR.size(); i++){ - XSLFTextRun r1 = srcR.get(i); - XSLFTextRun r2 = tgtR.get(i); - r2.copy(r1); - } } + private static boolean doubleEquals(Double d1, Double d2) { + return (d1 == d2 || (d1 != null && d1.equals(d2))); + } + @Override public Double getDefaultFontSize() { CTTextCharacterProperties endPr = _p.getEndParaRPr(); 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 74685ec1b..7a5c78b19 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java @@ -82,7 +82,8 @@ public class XSLFTextRun implements TextRun { return _r; } - public void setFontColor(Color color){ + @Override + public void setFontColor(Color color) { CTTextCharacterProperties rPr = getRPr(); CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill(); CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr(); @@ -96,6 +97,7 @@ public class XSLFTextRun implements TextRun { } + @Override public Color getFontColor(){ final XSLFTheme theme = _p.getParentShape().getSheet().getTheme(); CTShapeStyle style = _p.getParentShape().getSpStyle(); @@ -119,17 +121,13 @@ public class XSLFTextRun implements TextRun { return fetcher.getValue(); } - /** - * - * @param fontSize font size in points. - * The value of -1 unsets the Sz attribyte from the underlying xml bean - */ - public void setFontSize(double fontSize){ + @Override + public void setFontSize(Double fontSize){ CTTextCharacterProperties rPr = getRPr(); - if(fontSize == -1.0) { - if(rPr.isSetSz()) rPr.unsetSz(); + if(fontSize == null) { + if (rPr.isSetSz()) rPr.unsetSz(); } else { - if(fontSize < 1.0) { + if (fontSize < 1.0) { throw new IllegalArgumentException("Minimum font size is 1pt but was " + fontSize); } @@ -137,9 +135,6 @@ public class XSLFTextRun implements TextRun { } } - /** - * @return font size in points or null if font size is not set. - */ @Override public Double getFontSize(){ double scale = 1; 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 5b58adbf1..d1eb19e80 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java @@ -433,6 +433,10 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements TextShape protected abstract CTTextBody getTextBody(boolean create); + @Override + public void setPlaceholder(Placeholder placeholder) { + super.setPlaceholder(placeholder); + } public Placeholder getTextType(){ CTPlaceholder ph = getCTPlaceholder(); @@ -442,27 +446,6 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements TextShape return Placeholder.values()[val - 1]; } - - /** - * Specifies that the corresponding shape should be represented by the generating application - * as a placeholder. When a shape is considered a placeholder by the generating application - * it can have special properties to alert the user that they may enter content into the shape. - * Different types of placeholders are allowed and can be specified by using the placeholder - * type attribute for this element - * - * @param placeholder - */ - public void setPlaceholder(Placeholder placeholder){ - String xquery = "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:nvPr"; - CTApplicationNonVisualDrawingProps nv = selectProperty(CTApplicationNonVisualDrawingProps.class, xquery); - if (nv == null) return; - if(placeholder == null) { - if (nv.isSetPh()) nv.unsetPh(); - } else { - nv.addNewPh().setType(STPlaceholderType.Enum.forInt(placeholder.ordinal() + 1)); - } - } - @Override public double getTextHeight(){ DrawFactory drawFact = DrawFactory.getInstance(null); @@ -490,45 +473,55 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements TextShape @Override - void copy(XSLFShape sh){ - super.copy(sh); + void copy(XSLFShape other){ + super.copy(other); - XSLFTextShape tsh = (XSLFTextShape)sh; + XSLFTextShape otherTS = (XSLFTextShape)other; + CTTextBody otherTB = otherTS.getTextBody(false); + CTTextBody thisTB = getTextBody(true); + if (otherTB == null) { + return; + } + + thisTB.setBodyPr((CTTextBodyProperties)otherTB.getBodyPr().copy()); - boolean srcWordWrap = tsh.getWordWrap(); + if (thisTB.isSetLstStyle()) thisTB.unsetLstStyle(); + if (otherTB.isSetLstStyle()) { + thisTB.setLstStyle((CTTextListStyle)otherTB.getLstStyle().copy()); + } + + boolean srcWordWrap = otherTS.getWordWrap(); if(srcWordWrap != getWordWrap()){ setWordWrap(srcWordWrap); } - double leftInset = tsh.getLeftInset(); + double leftInset = otherTS.getLeftInset(); if(leftInset != getLeftInset()) { setLeftInset(leftInset); } - double rightInset = tsh.getRightInset(); + double rightInset = otherTS.getRightInset(); if(rightInset != getRightInset()) { setRightInset(rightInset); } - double topInset = tsh.getTopInset(); + double topInset = otherTS.getTopInset(); if(topInset != getTopInset()) { setTopInset(topInset); } - double bottomInset = tsh.getBottomInset(); + double bottomInset = otherTS.getBottomInset(); if(bottomInset != getBottomInset()) { setBottomInset(bottomInset); } - VerticalAlignment vAlign = tsh.getVerticalAlignment(); + VerticalAlignment vAlign = otherTS.getVerticalAlignment(); if(vAlign != getVerticalAlignment()) { setVerticalAlignment(vAlign); } - List srcP = tsh.getTextParagraphs(); - List tgtP = getTextParagraphs(); - for(int i = 0; i < srcP.size(); i++){ - XSLFTextParagraph p1 = srcP.get(i); - XSLFTextParagraph p2 = tgtP.get(i); - p2.copy(p1); + clearText(); + + for (XSLFTextParagraph srcP : otherTS.getTextParagraphs()) { + XSLFTextParagraph tgtP = addNewTextParagraph(); + tgtP.copy(srcP); } - } } \ No newline at end of file diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java index 6ae9606b7..7accdfdc2 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java @@ -125,7 +125,7 @@ public class TestXSLFAutoShape { p.setIndent(2.0); assertEquals(2.0, p.getIndent(), 0); assertTrue(p.getXmlObject().getPPr().isSetIndent()); - p.setIndent(-1d); + p.setIndent(null); assertNull(p.getIndent()); assertFalse(p.getXmlObject().getPPr().isSetIndent()); p.setIndent(10.0); @@ -225,7 +225,7 @@ public class TestXSLFAutoShape { assertEquals(1000, r.getXmlObject().getRPr().getSz()); r.setFontSize(12.5); assertEquals(1250, r.getXmlObject().getRPr().getSz()); - r.setFontSize(-1); + r.setFontSize(null); assertFalse(r.getXmlObject().getRPr().isSetSz()); assertFalse(r.getXmlObject().getRPr().isSetLatin()); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java index fdfb46ee9..3314855c7 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java @@ -143,7 +143,7 @@ public class TestXSLFSlide { assertFalse(r2.isItalic()); assertEquals(Color.white, r2.getFontColor()); assertEquals(new Color(148, 198, 0), sh2.getFillColor()); - assertEquals(new Color(74, 99, 0), sh2.getLineColor()); // slightly different from PowerPoint! + assertEquals(new Color(148, 198, 0), sh2.getLineColor()); // slightly different from PowerPoint! // the 5th slide has a picture and a texture fill XSLFSlide slide2 = ppt.createSlide().importContent(src.getSlides().get(4)); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java index 40e07a691..59b0dca95 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java @@ -78,7 +78,6 @@ public class TestXSLFTextBox { assertEquals(20.0, r.getFontSize(), 0); pPr.unsetSz(); // Should never be - assertEquals(-1.0, r.getFontSize(), 0); - + assertNull(r.getFontSize()); } } \ No newline at end of file diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java index 4d5173335..e71cf2419 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java @@ -30,6 +30,7 @@ import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.xslf.XSLFTestDataSamples; +import org.junit.Assume; import org.junit.Test; /** @@ -74,16 +75,16 @@ public class TestXSLFTextParagraph { DrawTextParagraphProxy dtp = new DrawTextParagraphProxy(p); - double leftInset = sh.getLeftInset(); - double rightInset = sh.getRightInset(); + Double leftInset = sh.getLeftInset(); + Double rightInset = sh.getRightInset(); assertEquals(7.2, leftInset, 0); assertEquals(7.2, rightInset, 0); - double leftMargin = p.getLeftMargin(); + Double leftMargin = p.getLeftMargin(); assertEquals(0.0, leftMargin, 0); - double indent = p.getIndent(); - assertEquals(0.0, indent, 0); // default + Double indent = p.getIndent(); + assertNull(indent); // default double expectedWidth; @@ -150,10 +151,7 @@ public class TestXSLFTextParagraph { @Test public void testBreakLines(){ String os = System.getProperty("os.name"); - if(os == null || !os.contains("Windows")) { - _logger.log(POILogger.WARN, "Skipping testBreakLines(), it is executed only on Windows machines"); - return; - } + Assume.assumeTrue("Skipping testBreakLines(), it is executed only on Windows machines", (os != null && os.contains("Windows"))); XMLSlideShow ppt = new XMLSlideShow(); XSLFSlide slide = ppt.createSlide(); @@ -162,7 +160,7 @@ public class TestXSLFTextParagraph { XSLFTextParagraph p = sh.addNewTextParagraph(); XSLFTextRun r = p.addNewTextRun(); r.setFontFamily("Arial"); // this should always be available - r.setFontSize(12); + r.setFontSize(12d); r.setText( "Paragraph formatting allows for more granular control " + "of text within a shape. Properties here apply to all text " + @@ -179,13 +177,13 @@ public class TestXSLFTextParagraph { lines = dtp.getLines(); assertEquals(4, lines.size()); - // descrease the shape width from 300 pt to 100 pt + // decrease the shape width from 300 pt to 100 pt sh.setAnchor(new Rectangle(50, 50, 100, 200)); dtp.breakText(graphics); lines = dtp.getLines(); assertEquals(12, lines.size()); - // descrease the shape width from 300 pt to 100 pt + // decrease the shape width from 300 pt to 100 pt sh.setAnchor(new Rectangle(50, 50, 600, 200)); dtp.breakText(graphics); lines = dtp.getLines(); @@ -224,12 +222,13 @@ public class TestXSLFTextParagraph { XSLFTextParagraph p2 = sh2.addNewTextParagraph(); XSLFTextRun r2 = p2.addNewTextRun(); r2.setFontFamily("serif"); // this should always be available - r2.setFontSize(30); + r2.setFontSize(30d); r2.setText("Apache\n"); XSLFTextRun r3 = p2.addNewTextRun(); r3.setFontFamily("serif"); // this should always be available - r3.setFontSize(10); + r3.setFontSize(10d); r3.setText("POI"); + dtp = new DrawTextParagraphProxy(p2); dtp.breakText(graphics); lines = dtp.getLines(); assertEquals(2, lines.size()); @@ -278,7 +277,7 @@ public class TestXSLFTextParagraph { p.setBulletFontColor(Color.red); assertEquals(Color.red, p.getBulletFontColor()); - assertEquals(100.0, p.getBulletFontSize(), 0); + assertNull(p.getBulletFontSize()); p.setBulletFontSize(200.); assertEquals(200., p.getBulletFontSize(), 0); p.setBulletFontSize(-20.); @@ -286,17 +285,21 @@ public class TestXSLFTextParagraph { assertEquals(72.0, p.getDefaultTabSize(), 0); - assertEquals(0.0, p.getIndent(), 0); + assertNull(p.getIndent()); p.setIndent(72.0); assertEquals(72.0, p.getIndent(), 0); - p.setIndent(-1.0); // the value of -1.0 resets to the defaults - assertEquals(0.0, p.getIndent(), 0); + p.setIndent(-1d); // the value of -1.0 resets to the defaults (not any more ...) + assertEquals(-1d, p.getIndent(), 0); + p.setIndent(null); + assertNull(p.getIndent()); assertEquals(0.0, p.getLeftMargin(), 0); p.setLeftMargin(72.0); assertEquals(72.0, p.getLeftMargin(), 0); p.setLeftMargin(-1.0); // the value of -1.0 resets to the defaults - assertEquals(0.0, p.getLeftMargin(), 0); + assertEquals(-1.0, p.getLeftMargin(), 0); + p.setLeftMargin(null); + assertEquals(0d, p.getLeftMargin(), 0); // default will be taken from master assertEquals(0, p.getIndentLevel()); p.setIndentLevel(1); @@ -304,19 +307,19 @@ public class TestXSLFTextParagraph { p.setIndentLevel(2); assertEquals(2, p.getIndentLevel()); - assertEquals(100., p.getLineSpacing(), 0); + assertNull(p.getLineSpacing()); p.setLineSpacing(200.); assertEquals(200.0, p.getLineSpacing(), 0); p.setLineSpacing(-15.); assertEquals(-15.0, p.getLineSpacing(), 0); - assertEquals(0., p.getSpaceAfter(), 0); + assertNull(p.getSpaceAfter()); p.setSpaceAfter(200.); assertEquals(200.0, p.getSpaceAfter(), 0); p.setSpaceAfter(-15.); assertEquals(-15.0, p.getSpaceAfter(), 0); - assertEquals(0., p.getSpaceBefore(), 0); + assertNull(p.getSpaceBefore()); p.setSpaceBefore(200.); assertEquals(200.0, p.getSpaceBefore(), 0); p.setSpaceBefore(-15.); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java index c3aabe051..de0d1a36a 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java @@ -24,6 +24,7 @@ import java.util.List; import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; import org.apache.poi.sl.usermodel.VerticalAlignment; import org.apache.poi.xslf.XSLFTestDataSamples; +import org.junit.Test; import org.openxmlformats.schemas.drawingml.x2006.main.*; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; @@ -33,6 +34,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; */ public class TestXSLFTextShape { + @Test public void testLayouts(){ XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("layouts.pptx"); @@ -612,7 +614,7 @@ public class TestXSLFTextShape { assertEquals("10", sldNum.getText()); } - + @Test public void testTitleStyles(){ XMLSlideShow ppt = new XMLSlideShow(); @@ -693,6 +695,7 @@ public class TestXSLFTextShape { assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); } + @Test public void testBodyStyles(){ XMLSlideShow ppt = new XMLSlideShow(); 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 3e85f75c5..f928124c4 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java @@ -59,7 +59,7 @@ public class TestXSLFTheme { } void slide1(XSLFSlide slide){ - assertEquals(Color.white, slide.getBackground().getFillColor()); + assertEquals(Color.WHITE, slide.getBackground().getFillColor()); XSLFTheme theme = slide.getTheme(); assertEquals("Office Theme", theme.getName()); @@ -75,7 +75,7 @@ public class TestXSLFTheme { void slide2(XSLFSlide slide){ // Background 2, darker 10% // YK: PPT shows slightly different color: new Color(221, 217, 195) - assertEquals(new Color(214, 212, 203), slide.getBackground().getFillColor()); + assertEquals(new Color(221, 217, 195), slide.getBackground().getFillColor()); } void slide3(XSLFSlide slide){ @@ -133,7 +133,7 @@ public class TestXSLFTheme { void slide7(XSLFSlide slide){ //YK: PPT reports a slightly different color: r=189,g=239,b=87 - assertEquals(new Color(182, 218, 108), slide.getBackground().getFillColor()); + assertEquals(new Color(189, 239, 87), slide.getBackground().getFillColor()); assertFalse(slide.getFollowMasterGraphics()); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java index 7ae20c669..3b22227b9 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java @@ -256,7 +256,7 @@ public final class PPGraphics2D extends Graphics2D implements Cloneable { txt.setText(s); HSLFTextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0); - rt.setFontSize(_font.getSize()); + rt.setFontSize((double)_font.getSize()); rt.setFontFamily(_font.getFamily()); if (getColor() != null) rt.setFontColor(getColor()); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java index 437ed274e..d12bfe3de 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java @@ -141,7 +141,7 @@ public final class HSLFTextRun implements TextRun { * @param propName The name of the Paragraph TextProp * @param val The value to set for the TextProp */ - public void setCharTextPropVal(String propName, int val) { + public void setCharTextPropVal(String propName, Integer val) { setPropVal(characterStyle, propName, val); } @@ -251,20 +251,17 @@ public final class HSLFTextRun implements TextRun { setPropVal(characterStyle, "superscript", val); } - /** - * Gets the font size - */ + @Override public Double getFontSize() { TextProp tp = getPropVal(characterStyle, "font.size", parentParagraph); return tp == null ? null : (double)tp.getValue(); } - /** - * Sets the font size - */ - public void setFontSize(int fontSize) { - setCharTextPropVal("font.size", fontSize); + @Override + public void setFontSize(Double fontSize) { + Integer iFontSize = (fontSize == null) ? null : fontSize.intValue(); + setCharTextPropVal("font.size", iFontSize); } /** diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java index 234fee1d0..16562b82a 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java @@ -18,12 +18,11 @@ package org.apache.poi.sl.draw; import static org.apache.poi.sl.usermodel.PaintStyle.TRANSPARENT_PAINT; + import java.awt.*; import java.awt.MultipleGradientPaint.ColorSpaceType; import java.awt.MultipleGradientPaint.CycleMethod; -import java.awt.Shape; import java.awt.geom.*; -import java.awt.image.*; import java.io.IOException; import java.io.InputStream; @@ -35,7 +34,13 @@ import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +/** + * This class handles color transformations + * + * @see HSL code taken from Java Tips Weblog + */ public class DrawPaint { + // HSL code is public domain - see https://tips4java.wordpress.com/contact-us/ private final static POILogger LOG = POILogFactory.getLogger(DrawPaint.class); @@ -126,7 +131,7 @@ public class DrawPaint { } result = applyAlpha(result, color); - result = applyLuminanace(result, color); + result = applyLuminance(result, color); result = applyShade(result, color); result = applyTint(result, color); @@ -135,7 +140,7 @@ public class DrawPaint { protected static Color applyAlpha(Color c, ColorStyle fc) { int alpha = c.getAlpha(); - return (alpha == 0 || alpha == -1) ? c : new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha); + return (alpha == 255) ? c : new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha); } /** @@ -145,8 +150,10 @@ public class DrawPaint { * @param lumMod luminance modulation in the range [0..100000] * @param lumOff luminance offset in the range [0..100000] * @return modified color + * + * @see Using Office Open XML to Customize Document Formatting in the 2007 Office System */ - protected static Color applyLuminanace(Color c, ColorStyle fc) { + protected static Color applyLuminance(Color c, ColorStyle fc) { int lumMod = fc.getLumMod(); if (lumMod == -1) lumMod = 100000; @@ -155,24 +162,33 @@ public class DrawPaint { if (lumMod == 100000 && lumOff == 0) return c; - int r = c.getRed(); - int g = c.getGreen(); - int b = c.getBlue(); + // The lumMod value is the percent luminance. A lumMod value of "60000", + // is 60% of the luminance of the original color. + // When the color is a shade of the original theme color, the lumMod + // attribute is the only one of the tags shown here that appears. + // The tag appears after the tag when the color is a + // tint of the original. The lumOff value always equals 1-lumMod, which is used in the tint calculation + // + // Despite having different ways to display the tint and shade percentages, + // all of the programs use the same method to calculate the resulting color. + // Convert the original RGB value to HSL ... and then adjust the luminance (L) + // with one of the following equations before converting the HSL value back to RGB. + // (The % tint in the following equations refers to the tint, themetint, themeshade, + // or lumMod values, as applicable.) + // + // For a shade, the equation is luminance * %tint. + // + // For a tint, the equation is luminance * %tint + (1-%tint). + // (Note that 1-%tint is equal to the lumOff value in DrawingML.) - float red,green,blue; + double fLumOff = lumOff / 100000d; + double fLumMod = lumMod / 100000d; - if (lumOff > 0) { - float flumOff = lumOff / 100000.f; - red = (255.f - r) * (1.f - flumOff) + r; - green = (255.f - g) * flumOff + g; - blue = (255.f - b) * flumOff + b; - } else { - float flumMod = lumMod / 100000.f; - red = r * flumMod; - green = g * flumMod; - blue = b * flumMod; - } - return new Color(Math.round(red), Math.round(green), Math.round(blue), c.getAlpha()); + double hsl[] = RGB2HSL(c); + hsl[2] = hsl[2]*fLumMod+fLumOff; + + Color c2 = HSL2RGB(hsl[0], hsl[1], hsl[2], c.getAlpha()/255d); + return c2; } /** @@ -302,165 +318,127 @@ public class DrawPaint { } } - public static class PathGradientPaint implements Paint { - - // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html - protected final Color colors[]; - protected final float fractions[]; - protected final int capStyle; - protected final int joinStyle; - protected final int transparency; - - - public PathGradientPaint(Color colors[], float fractions[]) { - this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); - } - - public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) { - this.colors = colors; - this.fractions = fractions; - this.capStyle = capStyle; - this.joinStyle = joinStyle; - - // determine transparency - boolean opaque = true; - for (int i = 0; i < colors.length; i++){ - opaque = opaque && (colors[i].getAlpha() == 0xff); - } - this.transparency = opaque ? OPAQUE : TRANSLUCENT; - } - - public PaintContext createContext(ColorModel cm, - Rectangle deviceBounds, - Rectangle2D userBounds, - AffineTransform transform, - RenderingHints hints) { - return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints); - } - - public int getTransparency() { - return transparency; + /** + * Convert HSL values to a RGB Color. + * + * @param h Hue is specified as degrees in the range 0 - 360. + * @param s Saturation is specified as a percentage in the range 1 - 100. + * @param l Luminance is specified as a percentage in the range 1 - 100. + * @param alpha the alpha value between 0 - 1 + * + * @returns the RGB Color object + */ + private static Color HSL2RGB(double h, double s, double l, double alpha) { + if (s <0.0f || s > 100.0f) { + String message = "Color parameter outside of expected range - Saturation"; + throw new IllegalArgumentException( message ); } - class PathGradientContext implements PaintContext { - protected final Rectangle deviceBounds; - protected final Rectangle2D userBounds; - protected final AffineTransform xform; - protected final RenderingHints hints; - - /** - * for POI: the shape will be only known when the subclasses determines the concrete implementation - * in the draw/-content method, so we need to postpone the setting/creation as long as possible - **/ - protected final Shape shape; - protected final PaintContext pCtx; - protected final int gradientSteps; - WritableRaster raster; - - public PathGradientContext( - ColorModel cm - , Rectangle deviceBounds - , Rectangle2D userBounds - , AffineTransform xform - , RenderingHints hints - ) { - shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE); - if (shape == null) { - throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint PathGradientPaint.GRADIANT_SHAPE."); - } - - this.deviceBounds = deviceBounds; - this.userBounds = userBounds; - this.xform = xform; - this.hints = hints; - - gradientSteps = getGradientSteps(shape); - - Point2D start = new Point2D.Double(0, 0); - Point2D end = new Point2D.Double(gradientSteps, 0); - LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform()); - - Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1); - pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints); - } - - public void dispose() {} - - public ColorModel getColorModel() { - return pCtx.getColorModel(); - } - - public Raster getRaster(int xOffset, int yOffset, int w, int h) { - ColorModel cm = getColorModel(); - if (raster == null) createRaster(); - - // TODO: eventually use caching here - WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h); - Rectangle2D childRect = new Rectangle2D.Double(xOffset, yOffset, w, h); - if (!childRect.intersects(deviceBounds)) { - // usually doesn't happen ... - return childRaster; - } - - Rectangle2D destRect = new Rectangle2D.Double(); - Rectangle2D.intersect(childRect, deviceBounds, destRect); - int dx = (int)(destRect.getX()-deviceBounds.getX()); - int dy = (int)(destRect.getY()-deviceBounds.getY()); - int dw = (int)destRect.getWidth(); - int dh = (int)destRect.getHeight(); - Object data = raster.getDataElements(dx, dy, dw, dh, null); - dx = (int)(destRect.getX()-childRect.getX()); - dy = (int)(destRect.getY()-childRect.getY()); - childRaster.setDataElements(dx, dy, dw, dh, data); - - return childRaster; - } - - protected int getGradientSteps(Shape shape) { - Rectangle rect = shape.getBounds(); - int lower = 1; - int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0); - while (lower < upper-1) { - int mid = lower + (upper - lower) / 2; - BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle); - Area area = new Area(bs.createStrokedShape(shape)); - if (area.isSingular()) { - upper = mid; - } else { - lower = mid; - } - } - return upper; - } - - - - protected void createRaster() { - ColorModel cm = getColorModel(); - raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight()); - BufferedImage img = new BufferedImage(cm, raster, false, null); - Graphics2D graphics = img.createGraphics(); - graphics.setRenderingHints(hints); - graphics.translate(-deviceBounds.getX(), -deviceBounds.getY()); - graphics.transform(xform); - - Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1); - int rgb[] = new int[cm.getNumComponents()]; - - for (int i = gradientSteps-1; i>=0; i--) { - img2.getPixel(i, 0, rgb); - Color c = new Color(rgb[0],rgb[1],rgb[2]); - if (rgb.length == 4) { - // it doesn't work to use just a color with transparency ... - graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f)); - } - graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle)); - graphics.setColor(c); - graphics.draw(shape); - } - - graphics.dispose(); - } + if (l <0.0f || l > 100.0f) { + String message = "Color parameter outside of expected range - Luminance"; + throw new IllegalArgumentException( message ); } + + if (alpha <0.0f || alpha > 1.0f) { + String message = "Color parameter outside of expected range - Alpha"; + throw new IllegalArgumentException( message ); + } + + // Formula needs all values between 0 - 1. + + h = h % 360.0f; + h /= 360f; + s /= 100f; + l /= 100f; + + double q = (l < 0.5d) + ? l * (1d + s) + : (l + s) - (s * l); + + double p = 2d * l - q; + + double r = Math.max(0, HUE2RGB(p, q, h + (1.0d / 3.0d))); + double g = Math.max(0, HUE2RGB(p, q, h)); + double b = Math.max(0, HUE2RGB(p, q, h - (1.0d / 3.0d))); + + r = Math.min(r, 1.0d); + g = Math.min(g, 1.0d); + b = Math.min(b, 1.0d); + + return new Color((float)r, (float)g, (float)b, (float)alpha); } -} + + private static double HUE2RGB(double p, double q, double h) { + if (h < 0d) h += 1d; + + if (h > 1d) h -= 1d; + + if (6d * h < 1d) { + return p + ((q - p) * 6d * h); + } + + if (2d * h < 1d) { + return q; + } + + if (3d * h < 2d) { + return p + ( (q - p) * 6d * ((2.0d / 3.0d) - h) ); + } + + return p; + } + + + /** + * Convert a RGB Color to it corresponding HSL values. + * + * @return an array containing the 3 HSL values. + */ + private static double[] RGB2HSL(Color color) + { + // Get RGB values in the range 0 - 1 + + float[] rgb = color.getRGBColorComponents( null ); + double r = rgb[0]; + double g = rgb[1]; + double b = rgb[2]; + + // Minimum and Maximum RGB values are used in the HSL calculations + + double min = Math.min(r, Math.min(g, b)); + double max = Math.max(r, Math.max(g, b)); + + // Calculate the Hue + + double h = 0; + + if (max == min) { + h = 0; + } else if (max == r) { + h = ((60d * (g - b) / (max - min)) + 360d) % 360d; + } else if (max == g) { + h = (60d * (b - r) / (max - min)) + 120d; + } else if (max == b) { + h = (60d * (r - g) / (max - min)) + 240d; + } + + // Calculate the Luminance + + double l = (max + min) / 2d; + + // Calculate the Saturation + + double s = 0; + + if (max == min) { + s = 0; + } else if (l <= .5d) { + s = (max - min) / (max + min); + } else { + s = (max - min) / (2d - max - min); + } + + return new double[] {h, s * 100, l * 100}; + } + +} \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java index 6b118617e..e2db501e8 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -34,7 +34,6 @@ import org.apache.poi.util.Units; public class DrawTextParagraph implements Drawable { protected TextParagraph paragraph; double x, y; - protected Insets2D insets = new Insets2D(0,0,0,0); protected List lines = new ArrayList(); protected String rawText; protected DrawTextFragment bullet; @@ -49,14 +48,6 @@ public class DrawTextParagraph implements Drawable { this.paragraph = paragraph; } - public Insets2D getInsets() { - return insets; - } - - public void setInsets(Insets2D insets) { - this.insets.set(insets.top, insets.left, insets.bottom, insets.right); - } - public void setPosition(double x, double y) { // TODO: replace it, by applyTransform???? this.x = x; @@ -78,6 +69,7 @@ public class DrawTextParagraph implements Drawable { public void draw(Graphics2D graphics){ if (lines.isEmpty()) return; + Insets2D insets = paragraph.getParentShape().getInsets(); double leftInset = insets.left; double rightInset = insets.right; double penY = y; @@ -336,6 +328,7 @@ public class DrawTextParagraph implements Drawable { protected double getWrappingWidth(boolean firstLine, Graphics2D graphics){ // internal margins for the text box + Insets2D insets = paragraph.getParentShape().getInsets(); double leftInset = insets.left; double rightInset = insets.right; diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java index 77927bdc6..5862ac598 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java @@ -119,7 +119,6 @@ public class DrawTextShape autoNbrIdx) autoNbrIdx = startAt; } dp.setAutoNumberingIdx(autoNbrIdx); - dp.setInsets(shapePadding); dp.breakText(graphics); if (!isFirstLine) { diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/PathGradientPaint.java b/src/scratchpad/src/org/apache/poi/sl/draw/PathGradientPaint.java new file mode 100644 index 000000000..c5ad799f4 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/sl/draw/PathGradientPaint.java @@ -0,0 +1,186 @@ +/* ==================================================================== + 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.sl.draw; + +import java.awt.*; +import java.awt.MultipleGradientPaint.ColorSpaceType; +import java.awt.MultipleGradientPaint.CycleMethod; +import java.awt.geom.*; +import java.awt.image.*; + +class PathGradientPaint implements Paint { + + // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html + protected final Color colors[]; + protected final float fractions[]; + protected final int capStyle; + protected final int joinStyle; + protected final int transparency; + + + public PathGradientPaint(Color colors[], float fractions[]) { + this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); + } + + public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) { + this.colors = colors; + this.fractions = fractions; + this.capStyle = capStyle; + this.joinStyle = joinStyle; + + // determine transparency + boolean opaque = true; + for (int i = 0; i < colors.length; i++){ + opaque = opaque && (colors[i].getAlpha() == 0xff); + } + this.transparency = opaque ? OPAQUE : TRANSLUCENT; + } + + public PaintContext createContext(ColorModel cm, + Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform transform, + RenderingHints hints) { + return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints); + } + + public int getTransparency() { + return transparency; + } + + class PathGradientContext implements PaintContext { + protected final Rectangle deviceBounds; + protected final Rectangle2D userBounds; + protected final AffineTransform xform; + protected final RenderingHints hints; + + /** + * for POI: the shape will be only known when the subclasses determines the concrete implementation + * in the draw/-content method, so we need to postpone the setting/creation as long as possible + **/ + protected final Shape shape; + protected final PaintContext pCtx; + protected final int gradientSteps; + WritableRaster raster; + + public PathGradientContext( + ColorModel cm + , Rectangle deviceBounds + , Rectangle2D userBounds + , AffineTransform xform + , RenderingHints hints + ) { + shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE); + if (shape == null) { + throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint PathGradientPaint.GRADIANT_SHAPE."); + } + + this.deviceBounds = deviceBounds; + this.userBounds = userBounds; + this.xform = xform; + this.hints = hints; + + gradientSteps = getGradientSteps(shape); + + Point2D start = new Point2D.Double(0, 0); + Point2D end = new Point2D.Double(gradientSteps, 0); + LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform()); + + Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1); + pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints); + } + + public void dispose() {} + + public ColorModel getColorModel() { + return pCtx.getColorModel(); + } + + public Raster getRaster(int xOffset, int yOffset, int w, int h) { + ColorModel cm = getColorModel(); + if (raster == null) createRaster(); + + // TODO: eventually use caching here + WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h); + Rectangle2D childRect = new Rectangle2D.Double(xOffset, yOffset, w, h); + if (!childRect.intersects(deviceBounds)) { + // usually doesn't happen ... + return childRaster; + } + + Rectangle2D destRect = new Rectangle2D.Double(); + Rectangle2D.intersect(childRect, deviceBounds, destRect); + int dx = (int)(destRect.getX()-deviceBounds.getX()); + int dy = (int)(destRect.getY()-deviceBounds.getY()); + int dw = (int)destRect.getWidth(); + int dh = (int)destRect.getHeight(); + Object data = raster.getDataElements(dx, dy, dw, dh, null); + dx = (int)(destRect.getX()-childRect.getX()); + dy = (int)(destRect.getY()-childRect.getY()); + childRaster.setDataElements(dx, dy, dw, dh, data); + + return childRaster; + } + + protected int getGradientSteps(Shape shape) { + Rectangle rect = shape.getBounds(); + int lower = 1; + int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0); + while (lower < upper-1) { + int mid = lower + (upper - lower) / 2; + BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle); + Area area = new Area(bs.createStrokedShape(shape)); + if (area.isSingular()) { + upper = mid; + } else { + lower = mid; + } + } + return upper; + } + + + + protected void createRaster() { + ColorModel cm = getColorModel(); + raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight()); + BufferedImage img = new BufferedImage(cm, raster, false, null); + Graphics2D graphics = img.createGraphics(); + graphics.setRenderingHints(hints); + graphics.translate(-deviceBounds.getX(), -deviceBounds.getY()); + graphics.transform(xform); + + Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1); + int rgb[] = new int[cm.getNumComponents()]; + + for (int i = gradientSteps-1; i>=0; i--) { + img2.getPixel(i, 0, rgb); + Color c = new Color(rgb[0],rgb[1],rgb[2]); + if (rgb.length == 4) { + // it doesn't work to use just a color with transparency ... + graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f)); + } + graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle)); + graphics.setColor(c); + graphics.draw(shape); + } + + graphics.dispose(); + } + } +} diff --git a/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java index bc1652afe..946bfc321 100644 --- a/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java +++ b/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java @@ -35,7 +35,18 @@ public interface TextRun { TextCap getTextCap(); Color getFontColor(); + void setFontColor(Color color); + + + /** + * @return font size in points or null if font size is not set. + */ Double getFontSize(); + + /** + * @param fontSize font size in points, if null the underlying fontsize will be unset + */ + void setFontSize(Double fontSize); String getFontFamily(); boolean isBold(); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSetBoldItalic.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSetBoldItalic.java index febf9e3b2..e593eb8b5 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSetBoldItalic.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSetBoldItalic.java @@ -47,7 +47,7 @@ public final class TestSetBoldItalic { HSLFTextBox txtbox = new HSLFTextBox(); rt = txtbox.getTextParagraphs().get(0).getTextRuns().get(0); txtbox.setText(val); - rt.setFontSize(42); + rt.setFontSize(42d); rt.setBold(true); rt.setItalic(true); rt.setUnderlined(false); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java index 4fde26896..19f0d5b03 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java @@ -194,7 +194,7 @@ public final class TestShapes { rt = txtbox.getTextParagraphs().get(0).getTextRuns().get(0); txtbox.setText(val); rt.setFontFamily("Arial"); - rt.setFontSize(42); + rt.setFontSize(42d); rt.setBold(true); rt.setItalic(true); rt.setUnderlined(false); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java index b57e9f503..8ad3ba6d7 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java @@ -168,7 +168,7 @@ public final class TestRichTextRun { // Change 2nd to different size and font assertEquals(2, ssRichB.getFontCollection().getChildRecords().length); // Default + TNR - rtrRb.setFontSize(18); + rtrRb.setFontSize(18d); rtrRb.setFontFamily("Courier"); assertEquals(3, ssRichB.getFontCollection().getChildRecords().length); // Default + TNR + Courier assertEquals(18, rtrRb.getFontSize(), 0); @@ -183,7 +183,7 @@ public final class TestRichTextRun { assertNotNull(rtr.getTextParagraph().getParagraphStyle()); // Change Font size - rtr.setFontSize(99); + rtr.setFontSize(99d); assertEquals(99, rtr.getFontSize(), 0); assertEquals(defaultFont, rtr.getFontFamily()); assertNotNull(rtr.getCharacterStyle()); @@ -191,7 +191,7 @@ public final class TestRichTextRun { assertEquals(1, ss.getFontCollection().getChildRecords().length); // Default // Change Font size and name - rtr.setFontSize(25); + rtr.setFontSize(25d); rtr.setFontFamily("Times New Roman"); assertEquals(25, rtr.getFontSize(), 0); assertEquals("Times New Roman", rtr.getFontFamily()); @@ -209,7 +209,7 @@ public final class TestRichTextRun { HSLFTextRun rtr = textParass.get(0).get(0).getTextRuns().get(0); rtr.setBold(true); - rtr.setFontSize(18); + rtr.setFontSize(18d); rtr.setFontFamily("Courier"); HSLFTextParagraph.storeText(textParass.get(0)); @@ -228,7 +228,7 @@ public final class TestRichTextRun { // Tweak existing one again, to ensure really worked rtr.setBold(false); - rtr.setFontSize(17); + rtr.setFontSize(17d); rtr.setFontFamily("CourierZZ"); // Check it took those changes @@ -511,7 +511,7 @@ public final class TestRichTextRun { "Multiline text"); HSLFTextParagraph rt = shape.getTextParagraphs().get(0); HSLFTextRun tr = rt.getTextRuns().get(0); - tr.setFontSize(42); + tr.setFontSize(42d); rt.setBullet(true); rt.setLeftMargin(50d); rt.setIndent(0d);