mirror of https://github.com/apache/poi.git
more progress with XSLF: made some methods publis as suggested on poi-dev, more tests
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1205902 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
01039a9d79
commit
d22f43cd43
|
@ -65,6 +65,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Encapsulates logic to translate DrawingML objects to Java2D
|
||||
|
@ -125,6 +126,8 @@ class RenderableShape {
|
|||
CTPathShadeProperties ps = gradFill.getPath();
|
||||
if(ps.getPath() == STPathShadeType.CIRCLE){
|
||||
paint = createRadialGradientPaint(gradFill, anchor, theme, phClr);
|
||||
} else if (ps.getPath() == STPathShadeType.SHAPE){
|
||||
paint = toRadialGradientPaint(gradFill, anchor, theme, phClr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +168,7 @@ class RenderableShape {
|
|||
return paint;
|
||||
}
|
||||
|
||||
private static Paint createLinearGradientPaint(
|
||||
private Paint createLinearGradientPaint(
|
||||
Graphics2D graphics,
|
||||
CTGradientFillProperties gradFill, Rectangle2D anchor,
|
||||
XSLFTheme theme, CTSchemeColor phClr) {
|
||||
|
@ -205,15 +208,16 @@ class RenderableShape {
|
|||
fractions[i] = stop.getPos() / 100000.f;
|
||||
}
|
||||
|
||||
AffineTransform grAt;
|
||||
if(gradFill.getRotWithShape()) grAt = new AffineTransform();
|
||||
else {
|
||||
// gradient fill is not rotated with the shape
|
||||
try {
|
||||
grAt = graphics.getTransform().createInverse();
|
||||
} catch (Exception e){
|
||||
// should not happen.
|
||||
grAt = new AffineTransform();
|
||||
AffineTransform grAt = new AffineTransform();
|
||||
if(gradFill.isSetRotWithShape() || !gradFill.getRotWithShape()) {
|
||||
double rotation = _shape.getRotation();
|
||||
if (rotation != 0.) {
|
||||
double centerX = anchor.getX() + anchor.getWidth() / 2;
|
||||
double centerY = anchor.getY() + anchor.getHeight() / 2;
|
||||
|
||||
grAt.translate(centerX, centerY);
|
||||
grAt.rotate(Math.toRadians(-rotation));
|
||||
grAt.translate(-centerX, -centerY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,6 +241,30 @@ class RenderableShape {
|
|||
return paint;
|
||||
}
|
||||
|
||||
/**
|
||||
* gradients with type=shape are enot supported by Java graphics.
|
||||
* We approximate it with a radial gradient.
|
||||
*/
|
||||
private static Paint toRadialGradientPaint(
|
||||
CTGradientFillProperties gradFill, Rectangle2D anchor,
|
||||
XSLFTheme theme, CTSchemeColor phClr) {
|
||||
|
||||
CTGradientStop[] gs = gradFill.getGsLst().getGsArray();
|
||||
Arrays.sort(gs, new Comparator<CTGradientStop>() {
|
||||
public int compare(CTGradientStop o1, CTGradientStop o2) {
|
||||
Integer pos1 = o1.getPos();
|
||||
Integer pos2 = o2.getPos();
|
||||
return pos1.compareTo(pos2);
|
||||
}
|
||||
});
|
||||
gs[1].setPos(50000);
|
||||
|
||||
CTGradientFillProperties g = CTGradientFillProperties.Factory.newInstance();
|
||||
g.set(gradFill);
|
||||
g.getGsLst().setGsArray(new CTGradientStop[]{gs[0], gs[1]});
|
||||
return createRadialGradientPaint(g, anchor, theme, phClr);
|
||||
}
|
||||
|
||||
private static Paint createRadialGradientPaint(
|
||||
CTGradientFillProperties gradFill, Rectangle2D anchor,
|
||||
XSLFTheme theme, CTSchemeColor phClr) {
|
||||
|
@ -498,7 +526,7 @@ class RenderableShape {
|
|||
if(shadow != null) for(Outline o : elems){
|
||||
if(o.getPath().isFilled()){
|
||||
if(fill != null) shadow.fill(graphics, o.getOutline());
|
||||
if(line != null) shadow.draw(graphics, o.getOutline());
|
||||
else if(line != null) shadow.draw(graphics, o.getOutline());
|
||||
}
|
||||
}
|
||||
// then fill the shape interior
|
||||
|
|
|
@ -32,7 +32,9 @@ import org.apache.poi.util.PackageHelper;
|
|||
import org.apache.poi.util.Units;
|
||||
import org.apache.poi.xslf.XSLFSlideShow;
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.apache.xmlbeans.XmlObject;
|
||||
import org.apache.xmlbeans.XmlOptions;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
|
||||
import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdList;
|
||||
|
@ -355,4 +357,15 @@ public class XMLSlideShow extends POIXMLDocument {
|
|||
return _tableStyles;
|
||||
}
|
||||
|
||||
CTTextParagraphProperties getDefaultParagraphStyle(int level) {
|
||||
XmlObject[] o = _presentation.selectPath(
|
||||
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
|
||||
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
|
||||
".//p:defaultTextStyle/a:lvl" +(level+1)+ "pPr");
|
||||
if(o.length == 1){
|
||||
return (CTTextParagraphProperties)o[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ package org.apache.poi.xslf.usermodel;
|
|||
import org.apache.xmlbeans.XmlObject;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTBackgroundFillStyleList;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground;
|
||||
|
||||
import java.awt.Color;
|
||||
|
@ -91,4 +93,15 @@ public class XSLFBackground extends XSLFSimpleShape {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* background does not have a associated transform.
|
||||
* we return a dummy transform object to prevent exceptions in inherited methods.
|
||||
*
|
||||
* @return dummy CTTransform2D bean
|
||||
*/
|
||||
@Override
|
||||
CTTransform2D getXfrm() {
|
||||
return CTTransform2D.Factory.newInstance();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import org.apache.poi.util.Beta;
|
|||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -78,9 +80,19 @@ public class XSLFImageRendener {
|
|||
Rectangle2D anchor) {
|
||||
try {
|
||||
BufferedImage img = ImageIO.read(data.getPackagePart().getInputStream());
|
||||
Number groupScale = (Number)graphics.getRenderingHint(XSLFRenderingHint.GROUP_SCALE);
|
||||
if(groupScale != null) {
|
||||
double sx = anchor.getWidth()/img.getWidth();
|
||||
double sy = anchor.getHeight()/img.getHeight();
|
||||
double tx = anchor.getX();
|
||||
double ty = anchor.getY();
|
||||
AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
|
||||
graphics.drawRenderedImage(img, at);
|
||||
} else {
|
||||
graphics.drawImage(img,
|
||||
(int) anchor.getX(), (int) anchor.getY(),
|
||||
(int) anchor.getWidth(), (int) anchor.getHeight(), null);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
|
|
|
@ -44,7 +44,11 @@ public class XSLFShadow extends XSLFSimpleShape {
|
|||
|
||||
public void fill(Graphics2D graphics, Shape outline) {
|
||||
|
||||
double angle = getAngle();
|
||||
double shapeRotation = _parent.getRotation();
|
||||
if(_parent.getFlipVertical()){
|
||||
shapeRotation += 180;
|
||||
}
|
||||
double angle = getAngle() - shapeRotation;
|
||||
double dist = getDistance();
|
||||
double dx = dist * Math.cos(Math.toRadians(angle));
|
||||
double dy = dist * Math.sin(Math.toRadians(angle));
|
||||
|
|
|
@ -413,6 +413,14 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements Iterable<X
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return background for this sheet
|
||||
*/
|
||||
public XSLFBackground getBackground() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this sheet into the supplied graphics object
|
||||
*
|
||||
|
|
|
@ -154,7 +154,7 @@ public abstract class XSLFSimpleShape extends XSLFShape {
|
|||
return _ph;
|
||||
}
|
||||
|
||||
private CTTransform2D getXfrm() {
|
||||
CTTransform2D getXfrm() {
|
||||
PropertyFetcher<CTTransform2D> fetcher = new PropertyFetcher<CTTransform2D>() {
|
||||
public boolean fetch(XSLFSimpleShape shape) {
|
||||
CTShapeProperties pr = shape.getSpPr();
|
||||
|
@ -561,7 +561,7 @@ public abstract class XSLFSimpleShape extends XSLFShape {
|
|||
*
|
||||
* The following order of inheritance is assumed:
|
||||
* <p>
|
||||
* slide <-- slideLayout <-- slideMaster <-- default styles in the slideMaster
|
||||
* slide <-- slideLayout <-- slideMaster
|
||||
* </p>
|
||||
*
|
||||
* @param visitor the object that collects the desired property
|
||||
|
@ -571,24 +571,21 @@ public abstract class XSLFSimpleShape extends XSLFShape {
|
|||
boolean ok = visitor.fetch(this);
|
||||
|
||||
XSLFSimpleShape masterShape;
|
||||
XSLFSheet layout = getSheet().getMasterSheet();
|
||||
if (layout != null) {
|
||||
if (!ok) {
|
||||
// first try to fetch from the slide layout
|
||||
XSLFSheet masterSheet = getSheet().getMasterSheet();
|
||||
CTPlaceholder ph = getCTPlaceholder();
|
||||
if (ph != null) {
|
||||
masterShape = layout.getPlaceholder(ph);
|
||||
|
||||
if (masterSheet != null && ph != null) {
|
||||
if (!ok) {
|
||||
masterShape = masterSheet.getPlaceholder(ph);
|
||||
if (masterShape != null) {
|
||||
ok = visitor.fetch(masterShape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try slide master
|
||||
if (!ok ) {
|
||||
int textType;
|
||||
CTPlaceholder ph = getCTPlaceholder();
|
||||
if (ph == null || !ph.isSetType()) textType = STPlaceholderType.INT_BODY;
|
||||
if ( !ph.isSetType()) textType = STPlaceholderType.INT_BODY;
|
||||
else {
|
||||
switch (ph.getType().intValue()) {
|
||||
case STPlaceholderType.INT_TITLE:
|
||||
|
@ -605,7 +602,7 @@ public abstract class XSLFSimpleShape extends XSLFShape {
|
|||
break;
|
||||
}
|
||||
}
|
||||
XSLFSheet master = layout.getMasterSheet();
|
||||
XSLFSheet master = masterSheet.getMasterSheet();
|
||||
if (master != null) {
|
||||
masterShape = master.getPlaceholderByType(textType);
|
||||
if (masterShape != null) {
|
||||
|
|
|
@ -184,23 +184,14 @@ public final class XSLFSlide extends XSLFSheet {
|
|||
*
|
||||
* @return the information about background appearance of this slide
|
||||
*/
|
||||
@Override
|
||||
public XSLFBackground getBackground() {
|
||||
|
||||
|
||||
if(_slide.getCSld().isSetBg()) {
|
||||
return new XSLFBackground(_slide.getCSld().getBg(), this);
|
||||
CTBackground bg = _slide.getCSld().getBg();
|
||||
if(bg != null) {
|
||||
return new XSLFBackground(bg, this);
|
||||
} else {
|
||||
return getMasterSheet().getBackground();
|
||||
}
|
||||
|
||||
XSLFSlideLayout layout = getMasterSheet();
|
||||
if(layout.getXmlObject().getCSld().isSetBg()) {
|
||||
return new XSLFBackground(layout.getXmlObject().getCSld().getBg(), this);
|
||||
}
|
||||
|
||||
XSLFSlideMaster master = layout.getMasterSheet();
|
||||
if(master.getXmlObject().getCSld().isSetBg()) {
|
||||
return new XSLFBackground(master.getXmlObject().getCSld().getBg(), this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ 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.openxmlformats.schemas.presentationml.x2006.main.CTBackground;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideLayout;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.SldLayoutDocument;
|
||||
|
@ -115,6 +116,17 @@ public class XSLFSlideLayout extends XSLFSheet {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public XSLFBackground getBackground() {
|
||||
CTBackground bg = _layout.getCSld().getBg();
|
||||
if(bg != null) {
|
||||
return new XSLFBackground(bg, this);
|
||||
} else {
|
||||
return getMasterSheet().getBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy placeholders from this layout to the destination slide
|
||||
*
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.poi.util.Beta;
|
|||
import org.apache.xmlbeans.XmlException;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterTextStyles;
|
||||
|
@ -167,4 +168,14 @@ import java.util.Map;
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XSLFBackground getBackground() {
|
||||
CTBackground bg = _slide.getCSld().getBg();
|
||||
if(bg != null) {
|
||||
return new XSLFBackground(bg, this);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -570,7 +570,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
|
|||
|
||||
/**
|
||||
*
|
||||
* @return the text level of this paragraph. Default is 0.
|
||||
* @return the text level of this paragraph (0-based). Default is 0.
|
||||
*/
|
||||
public int getLevel(){
|
||||
CTTextParagraphProperties pr = _p.getPPr();
|
||||
|
@ -898,11 +898,9 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
|
|||
return _lines;
|
||||
}
|
||||
|
||||
CTTextParagraphProperties getDefaultStyle(){
|
||||
CTTextParagraphProperties getDefaultMasterStyle(){
|
||||
CTPlaceholder ph = _shape.getCTPlaceholder();
|
||||
String defaultStyleSelector;
|
||||
if(ph == null) defaultStyleSelector = "otherStyle";
|
||||
else {
|
||||
switch(ph.getType().intValue()){
|
||||
case STPlaceholderType.INT_TITLE:
|
||||
case STPlaceholderType.INT_CTR_TITLE:
|
||||
|
@ -917,10 +915,10 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
|
|||
defaultStyleSelector = "bodyStyle";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int level = getLevel();
|
||||
|
||||
// wind up and find the root master sheet which must be slide master
|
||||
XSLFSheet masterSheet = _shape.getSheet();
|
||||
while (masterSheet.getMasterSheet() != null){
|
||||
masterSheet = masterSheet.getMasterSheet();
|
||||
|
@ -947,10 +945,19 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
|
|||
XSLFTextShape shape = getParentShape();
|
||||
ok = shape.fetchShapeProperty(visitor);
|
||||
if(!ok){
|
||||
CTTextParagraphProperties defaultProps = getDefaultStyle();
|
||||
CTPlaceholder ph = shape.getCTPlaceholder();
|
||||
if(ph == null){
|
||||
// if it is a plain text box then take defaults from presentation.xml
|
||||
XMLSlideShow ppt = getParentShape().getSheet().getSlideShow();
|
||||
CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getLevel());
|
||||
if(themeProps != null) ok = visitor.fetch(themeProps);
|
||||
} else {
|
||||
// defaults for placeholders are defined in the slide master
|
||||
CTTextParagraphProperties defaultProps = getDefaultMasterStyle();
|
||||
if(defaultProps != null) ok = visitor.fetch(defaultProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ 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.STSchemeColorVal;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.font.FontRenderContext;
|
||||
|
@ -468,13 +469,25 @@ public class XSLFTextRun {
|
|||
XSLFTextShape shape = _p.getParentShape();
|
||||
ok = shape.fetchShapeProperty(fetcher);
|
||||
if(!ok){
|
||||
CTTextParagraphProperties defaultProps = _p.getDefaultStyle();
|
||||
CTPlaceholder ph = shape.getCTPlaceholder();
|
||||
if(ph == null){
|
||||
// if it is a plain text box then take defaults from presentation.xml
|
||||
XMLSlideShow ppt = shape.getSheet().getSlideShow();
|
||||
CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(_p.getLevel());
|
||||
if(themeProps != null) {
|
||||
fetcher.isFetchingFromMaster = true;
|
||||
ok = fetcher.fetch(themeProps);
|
||||
}
|
||||
} else {
|
||||
// defaults for placeholders are defined in the slide master
|
||||
CTTextParagraphProperties defaultProps = _p.getDefaultMasterStyle();
|
||||
if(defaultProps != null) {
|
||||
fetcher.isFetchingFromMaster = true;
|
||||
ok = fetcher.fetch(defaultProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,11 @@ import java.util.List;
|
|||
public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<XSLFTextParagraph>{
|
||||
private final List<XSLFTextParagraph> _paragraphs;
|
||||
|
||||
/**
|
||||
* whether the text was broken into lines.
|
||||
*/
|
||||
private boolean _isTextBroken;
|
||||
|
||||
/*package*/ XSLFTextShape(XmlObject shape, XSLFSheet sheet) {
|
||||
super(shape, sheet);
|
||||
|
||||
|
@ -441,17 +446,23 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<
|
|||
/**
|
||||
* Compute the cumulative height occupied by the text
|
||||
*/
|
||||
private double getTextHeight(){
|
||||
public double getTextHeight(){
|
||||
// dry-run in a 1x1 image and return the vertical advance
|
||||
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
|
||||
return drawParagraphs(img.createGraphics(), 0, 0);
|
||||
Graphics2D graphics = img.createGraphics();
|
||||
breakText(graphics);
|
||||
return drawParagraphs(graphics, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* break the contained text into lines
|
||||
*/
|
||||
private void breakText(Graphics2D graphics){
|
||||
if(!_isTextBroken) {
|
||||
for(XSLFTextParagraph p : _paragraphs) p.breakText(graphics);
|
||||
|
||||
_isTextBroken = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;
|
|||
import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTColorScheme;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
@ -150,4 +151,17 @@ public class XSLFTheme extends POIXMLDocumentPart {
|
|||
public String getMinorFont(){
|
||||
return _theme.getThemeElements().getFontScheme().getMinorFont().getLatin().getTypeface();
|
||||
}
|
||||
|
||||
|
||||
CTTextParagraphProperties getDefaultParagraphStyle(){
|
||||
XmlObject[] o = _theme.selectPath(
|
||||
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
|
||||
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
|
||||
".//a:objectDefaults/a:spDef/a:lstStyle/a:defPPr");
|
||||
if(o.length == 1){
|
||||
return (CTTextParagraphProperties)o[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.poi.xslf.usermodel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
|
||||
|
||||
/**
|
||||
* @author Yegor Kozlov
|
||||
|
@ -33,5 +34,33 @@ public class TestXSLFTextBox extends TestCase {
|
|||
assertEquals(Placeholder.TITLE, shape.getTextType());
|
||||
shape.setPlaceholder(null);
|
||||
assertNull(shape.getTextType());
|
||||
shape.setText("Apache POI");
|
||||
}
|
||||
|
||||
/**
|
||||
* text box inherits default text proeprties from presentation.xml
|
||||
*/
|
||||
public void testDefaultTextStyle() {
|
||||
XMLSlideShow ppt = new XMLSlideShow();
|
||||
XSLFSlide slide = ppt.createSlide();
|
||||
|
||||
// default character properties for paragraphs with level=1
|
||||
CTTextCharacterProperties pPr = ppt.getCTPresentation().getDefaultTextStyle().getLvl1PPr().getDefRPr();
|
||||
|
||||
XSLFTextBox shape = slide.createTextBox();
|
||||
shape.setText("Apache POI");
|
||||
assertEquals(1, shape.getTextParagraphs().size());
|
||||
assertEquals(1, shape.getTextParagraphs().get(0).getTextRuns().size());
|
||||
|
||||
XSLFTextRun r = shape.getTextParagraphs().get(0).getTextRuns().get(0);
|
||||
|
||||
assertEquals(1800, pPr.getSz());
|
||||
assertEquals(18.0, r.getFontSize());
|
||||
assertEquals("Calibri", r.getFontFamily());
|
||||
|
||||
pPr.setSz(900);
|
||||
pPr.getLatin().setTypeface("Arial");
|
||||
assertEquals(9.0, r.getFontSize());
|
||||
assertEquals("Arial", r.getFontFamily());
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package org.apache.poi.xslf.usermodel;
|
|||
import junit.framework.TestCase;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.xslf.XSLFTestDataSamples;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
@ -105,22 +106,13 @@ public class TestXSLFTextParagraph extends TestCase {
|
|||
|
||||
/**
|
||||
* test breaking test into lines.
|
||||
* This test is platform-dependent and will run only if the test fonr (Arial)
|
||||
* is present in local graphics environment
|
||||
* This test requires that the Arial font is available and will run only on windows
|
||||
*/
|
||||
public void testBreakLines(){
|
||||
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
String testFont = "Arial";
|
||||
boolean testFontAvailable = false;
|
||||
for(String family : env.getAvailableFontFamilyNames()) {
|
||||
if(family.equals(testFont)) {
|
||||
testFontAvailable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!testFontAvailable) {
|
||||
_logger.log(POILogger.WARN, "the Arial font family is not available in local graphics environment");
|
||||
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;
|
||||
}
|
||||
|
||||
XMLSlideShow ppt = new XMLSlideShow();
|
||||
|
@ -198,4 +190,20 @@ public class TestXSLFTextParagraph extends TestCase {
|
|||
assertTrue(lines.get(0).getHeight() > lines.get(1).getHeight()*2);
|
||||
|
||||
}
|
||||
|
||||
public void testThemeInheritance(){
|
||||
XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("prProps.pptx");
|
||||
XSLFShape[] shapes = ppt.getSlides()[0].getShapes();
|
||||
XSLFTextShape sh1 = (XSLFTextShape)shapes[0];
|
||||
assertEquals("Apache", sh1.getText());
|
||||
assertEquals(TextAlign.CENTER, sh1.getTextParagraphs().get(0).getTextAlign());
|
||||
XSLFTextShape sh2 = (XSLFTextShape)shapes[1];
|
||||
assertEquals("Software", sh2.getText());
|
||||
assertEquals(TextAlign.CENTER, sh2.getTextParagraphs().get(0).getTextAlign());
|
||||
XSLFTextShape sh3 = (XSLFTextShape)shapes[2];
|
||||
assertEquals("Foundation", sh3.getText());
|
||||
assertEquals(TextAlign.CENTER, sh3.getTextParagraphs().get(0).getTextAlign());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ package org.apache.poi.xslf.usermodel;
|
|||
import junit.framework.TestCase;
|
||||
import org.apache.poi.xslf.XSLFTestDataSamples;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
|
||||
|
||||
|
@ -606,4 +609,288 @@ public class TestXSLFTextShape extends TestCase {
|
|||
XSLFTextShape sldNum = (XSLFTextShape)slide.getPlaceholderByType(STPlaceholderType.INT_SLD_NUM);
|
||||
assertEquals("10", sldNum.getText());
|
||||
}
|
||||
|
||||
|
||||
public void testTitleStyles(){
|
||||
XMLSlideShow ppt = new XMLSlideShow();
|
||||
|
||||
XSLFSlideMaster master = ppt.getSlideMasters()[0];
|
||||
XSLFTheme theme = master.getTheme();
|
||||
XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE);
|
||||
XSLFSlide slide = ppt.createSlide(layout) ;
|
||||
assertSame(layout, slide.getSlideLayout());
|
||||
assertSame(master, slide.getSlideMaster());
|
||||
|
||||
XSLFTextShape titleShape = (XSLFTextShape)slide.getPlaceholder(0);
|
||||
titleShape.setText("Apache POI");
|
||||
XSLFTextParagraph paragraph = titleShape.getTextParagraphs().get(0);
|
||||
XSLFTextRun textRun = paragraph.getTextRuns().get(0);
|
||||
|
||||
// level 1 : default title style on the master slide
|
||||
// /p:sldMaster/p:txStyles/p:titleStyle/a:lvl1pPr
|
||||
CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getTitleStyle().getLvl1PPr();
|
||||
CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr();
|
||||
assertEquals(4400, lv1CPr.getSz());
|
||||
assertEquals(44.0, textRun.getFontSize());
|
||||
assertEquals("+mj-lt", lv1CPr.getLatin().getTypeface());
|
||||
assertEquals("Calibri", theme.getMajorFont());
|
||||
assertEquals("Calibri", textRun.getFontFamily());
|
||||
lv1CPr.setSz(3200);
|
||||
assertEquals(32.0, textRun.getFontSize());
|
||||
lv1CPr.getLatin().setTypeface("Arial");
|
||||
assertEquals("Arial", textRun.getFontFamily());
|
||||
assertEquals(STTextAlignType.CTR, lv1PPr.getAlgn());
|
||||
assertEquals(TextAlign.CENTER, paragraph.getTextAlign());
|
||||
lv1PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, paragraph.getTextAlign());
|
||||
|
||||
// level 2: title placeholder on the master slide
|
||||
// /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="title"]
|
||||
XSLFTextShape tx2 = master.getPlaceholder(0);
|
||||
CTTextParagraphProperties lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr();
|
||||
CTTextCharacterProperties lv2CPr = lv2PPr.addNewDefRPr();
|
||||
lv2CPr.setSz(3300);
|
||||
assertEquals(33.0, textRun.getFontSize());
|
||||
lv2CPr.addNewLatin().setTypeface("Times");
|
||||
assertEquals("Times", textRun.getFontFamily());
|
||||
lv2PPr.setAlgn(STTextAlignType.R);
|
||||
assertEquals(TextAlign.RIGHT, paragraph.getTextAlign());
|
||||
|
||||
|
||||
// level 3: title placeholder on the slide layout
|
||||
// /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"]
|
||||
XSLFTextShape tx3 = layout.getPlaceholder(0);
|
||||
CTTextParagraphProperties lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr();
|
||||
CTTextCharacterProperties lv3CPr = lv3PPr.addNewDefRPr();
|
||||
lv3CPr.setSz(3400);
|
||||
assertEquals(34.0, textRun.getFontSize());
|
||||
lv3CPr.addNewLatin().setTypeface("Courier New");
|
||||
assertEquals("Courier New", textRun.getFontFamily());
|
||||
lv3PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, paragraph.getTextAlign());
|
||||
|
||||
// level 4: default text properties in the shape itself
|
||||
// ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr
|
||||
CTTextParagraphProperties lv4PPr = titleShape.getTextBody(true).getLstStyle().addNewLvl1PPr();
|
||||
CTTextCharacterProperties lv4CPr = lv4PPr.addNewDefRPr();
|
||||
lv4CPr.setSz(3500);
|
||||
assertEquals(35.0, textRun.getFontSize());
|
||||
lv4CPr.addNewLatin().setTypeface("Arial");
|
||||
assertEquals("Arial", textRun.getFontFamily());
|
||||
lv4PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, paragraph.getTextAlign());
|
||||
|
||||
// level 5: text properties are defined in the text run
|
||||
CTTextParagraphProperties lv5PPr = paragraph.getXmlObject().addNewPPr();
|
||||
CTTextCharacterProperties lv5CPr = textRun.getXmlObject().getRPr();
|
||||
lv5CPr.setSz(3600);
|
||||
assertEquals(36.0, textRun.getFontSize());
|
||||
lv5CPr.addNewLatin().setTypeface("Calibri");
|
||||
assertEquals("Calibri", textRun.getFontFamily());
|
||||
lv5PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, paragraph.getTextAlign());
|
||||
}
|
||||
|
||||
public void testBodyStyles(){
|
||||
XMLSlideShow ppt = new XMLSlideShow();
|
||||
|
||||
XSLFSlideMaster master = ppt.getSlideMasters()[0];
|
||||
XSLFTheme theme = master.getTheme();
|
||||
XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE_AND_CONTENT);
|
||||
XSLFSlide slide = ppt.createSlide(layout) ;
|
||||
assertSame(layout, slide.getSlideLayout());
|
||||
assertSame(master, slide.getSlideMaster());
|
||||
|
||||
XSLFTextShape tx1 = (XSLFTextShape)slide.getPlaceholder(1);
|
||||
tx1.clearText();
|
||||
|
||||
XSLFTextParagraph p1 = tx1.addNewTextParagraph();
|
||||
assertEquals(0, p1.getLevel());
|
||||
XSLFTextRun r1 = p1.addNewTextRun();
|
||||
r1.setText("Apache POI");
|
||||
|
||||
XSLFTextParagraph p2 = tx1.addNewTextParagraph();
|
||||
p2.setLevel(1);
|
||||
assertEquals(1, p2.getLevel());
|
||||
XSLFTextRun r2 = p2.addNewTextRun();
|
||||
r2.setText("HSLF");
|
||||
|
||||
XSLFTextParagraph p3 = tx1.addNewTextParagraph();
|
||||
p3.setLevel(2);
|
||||
assertEquals(2, p3.getLevel());
|
||||
XSLFTextRun r3 = p3.addNewTextRun();
|
||||
r3.setText("XSLF");
|
||||
|
||||
// level 1 : default title style on the master slide
|
||||
// /p:sldMaster/p:txStyles/p:bodyStyle/a:lvl1pPr
|
||||
CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl1PPr();
|
||||
CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr();
|
||||
CTTextParagraphProperties lv2PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl2PPr();
|
||||
CTTextCharacterProperties lv2CPr = lv2PPr.getDefRPr();
|
||||
CTTextParagraphProperties lv3PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl3PPr();
|
||||
CTTextCharacterProperties lv3CPr = lv3PPr.getDefRPr();
|
||||
// lv1
|
||||
assertEquals(3200, lv1CPr.getSz());
|
||||
assertEquals(32.0, r1.getFontSize());
|
||||
assertEquals("+mn-lt", lv1CPr.getLatin().getTypeface());
|
||||
assertEquals("Calibri", theme.getMinorFont());
|
||||
assertEquals("Calibri", r1.getFontFamily());
|
||||
lv1CPr.setSz(3300);
|
||||
assertEquals(33.0, r1.getFontSize());
|
||||
lv1CPr.getLatin().setTypeface("Arial");
|
||||
assertEquals("Arial", r1.getFontFamily());
|
||||
assertEquals(STTextAlignType.L, lv1PPr.getAlgn());
|
||||
assertEquals(TextAlign.LEFT, p1.getTextAlign());
|
||||
lv1PPr.setAlgn(STTextAlignType.R);
|
||||
assertEquals(TextAlign.RIGHT, p1.getTextAlign());
|
||||
//lv2
|
||||
assertEquals(2800, lv2CPr.getSz());
|
||||
assertEquals(28.0, r2.getFontSize());
|
||||
lv2CPr.setSz(3300);
|
||||
assertEquals(33.0, r2.getFontSize());
|
||||
lv2CPr.getLatin().setTypeface("Times");
|
||||
assertEquals("Times", r2.getFontFamily());
|
||||
assertEquals(STTextAlignType.L, lv2PPr.getAlgn());
|
||||
assertEquals(TextAlign.LEFT, p2.getTextAlign());
|
||||
lv2PPr.setAlgn(STTextAlignType.R);
|
||||
assertEquals(TextAlign.RIGHT, p2.getTextAlign());
|
||||
//lv3
|
||||
assertEquals(2400, lv3CPr.getSz());
|
||||
assertEquals(24.0, r3.getFontSize());
|
||||
lv3CPr.setSz(2500);
|
||||
assertEquals(25.0, r3.getFontSize());
|
||||
lv3CPr.getLatin().setTypeface("Courier New");
|
||||
assertEquals("Courier New", r3.getFontFamily());
|
||||
assertEquals(STTextAlignType.L, lv3PPr.getAlgn());
|
||||
assertEquals(TextAlign.LEFT, p3.getTextAlign());
|
||||
lv3PPr.setAlgn(STTextAlignType.R);
|
||||
assertEquals(TextAlign.RIGHT, p3.getTextAlign());
|
||||
|
||||
|
||||
// level 2: body placeholder on the master slide
|
||||
// /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="body"]
|
||||
XSLFTextShape tx2 = master.getPlaceholder(1);
|
||||
assertEquals(Placeholder.BODY, tx2.getTextType());
|
||||
|
||||
lv1PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr();
|
||||
lv1CPr = lv1PPr.addNewDefRPr();
|
||||
lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl2PPr();
|
||||
lv2CPr = lv2PPr.addNewDefRPr();
|
||||
lv3PPr = tx2.getTextBody(true).getLstStyle().addNewLvl3PPr();
|
||||
lv3CPr = lv3PPr.addNewDefRPr();
|
||||
|
||||
lv1CPr.setSz(3300);
|
||||
assertEquals(33.0, r1.getFontSize());
|
||||
lv1CPr.addNewLatin().setTypeface("Times");
|
||||
assertEquals("Times", r1.getFontFamily());
|
||||
lv1PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, p1.getTextAlign());
|
||||
|
||||
lv2CPr.setSz(3300);
|
||||
assertEquals(33.0, r2.getFontSize());
|
||||
lv2CPr.addNewLatin().setTypeface("Times");
|
||||
assertEquals("Times", r2.getFontFamily());
|
||||
lv2PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, p2.getTextAlign());
|
||||
|
||||
lv3CPr.setSz(3300);
|
||||
assertEquals(33.0, r3.getFontSize());
|
||||
lv3CPr.addNewLatin().setTypeface("Times");
|
||||
assertEquals("Times", r3.getFontFamily());
|
||||
lv3PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, p3.getTextAlign());
|
||||
|
||||
// level 3: body placeholder on the slide layout
|
||||
// /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"]
|
||||
XSLFTextShape tx3 = layout.getPlaceholder(1);
|
||||
assertEquals(Placeholder.BODY, tx2.getTextType());
|
||||
lv1PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr();
|
||||
lv1CPr = lv1PPr.addNewDefRPr();
|
||||
lv2PPr = tx3.getTextBody(true).getLstStyle().addNewLvl2PPr();
|
||||
lv2CPr = lv2PPr.addNewDefRPr();
|
||||
lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl3PPr();
|
||||
lv3CPr = lv3PPr.addNewDefRPr();
|
||||
|
||||
lv1CPr.setSz(3400);
|
||||
assertEquals(34.0, r1.getFontSize());
|
||||
lv1CPr.addNewLatin().setTypeface("Courier New");
|
||||
assertEquals("Courier New", r1.getFontFamily());
|
||||
lv1PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, p1.getTextAlign());
|
||||
|
||||
lv2CPr.setSz(3400);
|
||||
assertEquals(34.0, r2.getFontSize());
|
||||
lv2CPr.addNewLatin().setTypeface("Courier New");
|
||||
assertEquals("Courier New", r2.getFontFamily());
|
||||
lv2PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, p2.getTextAlign());
|
||||
|
||||
lv3CPr.setSz(3400);
|
||||
assertEquals(34.0, r3.getFontSize());
|
||||
lv3CPr.addNewLatin().setTypeface("Courier New");
|
||||
assertEquals("Courier New", r3.getFontFamily());
|
||||
lv3PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, p3.getTextAlign());
|
||||
|
||||
// level 4: default text properties in the shape itself
|
||||
// ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr
|
||||
lv1PPr = tx1.getTextBody(true).getLstStyle().addNewLvl1PPr();
|
||||
lv1CPr = lv1PPr.addNewDefRPr();
|
||||
lv2PPr = tx1.getTextBody(true).getLstStyle().addNewLvl2PPr();
|
||||
lv2CPr = lv2PPr.addNewDefRPr();
|
||||
lv3PPr = tx1.getTextBody(true).getLstStyle().addNewLvl3PPr();
|
||||
lv3CPr = lv3PPr.addNewDefRPr();
|
||||
|
||||
lv1CPr.setSz(3500);
|
||||
assertEquals(35.0, r1.getFontSize());
|
||||
lv1CPr.addNewLatin().setTypeface("Arial");
|
||||
assertEquals("Arial", r1.getFontFamily());
|
||||
lv1PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, p1.getTextAlign());
|
||||
|
||||
lv2CPr.setSz(3500);
|
||||
assertEquals(35.0, r2.getFontSize());
|
||||
lv2CPr.addNewLatin().setTypeface("Arial");
|
||||
assertEquals("Arial", r2.getFontFamily());
|
||||
lv2PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, p2.getTextAlign());
|
||||
|
||||
lv3CPr.setSz(3500);
|
||||
assertEquals(35.0, r3.getFontSize());
|
||||
lv3CPr.addNewLatin().setTypeface("Arial");
|
||||
assertEquals("Arial", r3.getFontFamily());
|
||||
lv3PPr.setAlgn(STTextAlignType.L);
|
||||
assertEquals(TextAlign.LEFT, p3.getTextAlign());
|
||||
|
||||
// level 5: text properties are defined in the text run
|
||||
lv1PPr = p1.getXmlObject().isSetPPr() ? p1.getXmlObject().getPPr() : p1.getXmlObject().addNewPPr();
|
||||
lv1CPr = r1.getXmlObject().getRPr();
|
||||
lv2PPr = p2.getXmlObject().isSetPPr() ? p2.getXmlObject().getPPr() : p2.getXmlObject().addNewPPr();
|
||||
lv2CPr = r2.getXmlObject().getRPr();
|
||||
lv3PPr = p3.getXmlObject().isSetPPr() ? p3.getXmlObject().getPPr() : p3.getXmlObject().addNewPPr();
|
||||
lv3CPr = r3.getXmlObject().getRPr();
|
||||
|
||||
lv1CPr.setSz(3600);
|
||||
assertEquals(36.0, r1.getFontSize());
|
||||
lv1CPr.addNewLatin().setTypeface("Calibri");
|
||||
assertEquals("Calibri", r1.getFontFamily());
|
||||
lv1PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, p1.getTextAlign());
|
||||
|
||||
lv2CPr.setSz(3600);
|
||||
assertEquals(36.0, r2.getFontSize());
|
||||
lv2CPr.addNewLatin().setTypeface("Calibri");
|
||||
assertEquals("Calibri", r2.getFontFamily());
|
||||
lv2PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, p2.getTextAlign());
|
||||
|
||||
lv3CPr.setSz(3600);
|
||||
assertEquals(36.0, r3.getFontSize());
|
||||
lv3CPr.addNewLatin().setTypeface("Calibri");
|
||||
assertEquals("Calibri", r3.getFontFamily());
|
||||
lv3PPr.setAlgn(STTextAlignType.CTR);
|
||||
assertEquals(TextAlign.CENTER, p3.getTextAlign());
|
||||
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue