123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- /*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ====================================================================
- */
-
- package org.apache.poi.xslf.usermodel;
-
- import org.apache.poi.openxml4j.opc.PackagePart;
- import org.apache.poi.openxml4j.opc.PackageRelationship;
- import org.apache.poi.util.Internal;
- import org.apache.poi.util.Units;
- import org.apache.poi.xslf.model.PropertyFetcher;
- import org.apache.poi.xslf.model.geom.Context;
- import org.apache.poi.xslf.model.geom.CustomGeometry;
- import org.apache.poi.xslf.model.geom.Guide;
- import org.apache.poi.xslf.model.geom.IAdjustableShape;
- import org.apache.poi.xslf.model.geom.Outline;
- import org.apache.poi.xslf.model.geom.Path;
- import org.apache.xmlbeans.XmlObject;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStop;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTNoFillProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTPathShadeProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
- import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType;
-
- import java.awt.AlphaComposite;
- import java.awt.BasicStroke;
- import java.awt.Color;
- import java.awt.GradientPaint;
- import java.awt.Graphics2D;
- import java.awt.Paint;
- import java.awt.Shape;
- import java.awt.Stroke;
- import java.awt.TexturePaint;
- import java.awt.geom.AffineTransform;
- import java.awt.geom.Point2D;
- import java.awt.geom.Rectangle2D;
- import java.awt.image.BufferedImage;
- import java.lang.reflect.Constructor;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Comparator;
-
- /**
- * Encapsulates logic to translate DrawingML objects to Java2D
- */
- @Internal
- class RenderableShape {
- public final static Color NO_PAINT = new Color(0xFF, 0xFF, 0xFF, 0);
-
- private XSLFSimpleShape _shape;
-
- public RenderableShape(XSLFSimpleShape shape){
- _shape = shape;
- }
-
- /**
- * Convert shape fill into java.awt.Paint. The result is either Color or
- * TexturePaint or GradientPaint or null
- *
- * @param graphics the target graphics
- * @param obj the xml to read. Must contain elements from the EG_ColorChoice group:
- * <code>
- * a:scrgbClr RGB Color Model - Percentage Variant
- * a:srgbClr RGB Color Model - Hex Variant
- * a:hslClr Hue, Saturation, Luminance Color Model
- * a:sysClr System Color
- * a:schemeClr Scheme Color
- * a:prstClr Preset Color
- * </code>
- *
- * @param phClr context color
- * @param parentPart the parent package part. Any external references (images, etc.) are resolved relative to it.
- *
- * @return the applied Paint or null if none was applied
- */
- public Paint selectPaint(Graphics2D graphics, XmlObject obj, CTSchemeColor phClr, PackagePart parentPart) {
- XSLFTheme theme = _shape.getSheet().getTheme();
- Rectangle2D anchor = _shape.getAnchor();
-
- Paint paint = null;
- if (obj instanceof CTNoFillProperties) {
- paint = NO_PAINT;
-
- }
- else if (obj instanceof CTSolidColorFillProperties) {
- CTSolidColorFillProperties solidFill = (CTSolidColorFillProperties) obj;
- XSLFColor c = new XSLFColor(solidFill, theme, phClr);
- paint = c.getColor();
- }
- else if (obj instanceof CTBlipFillProperties) {
- CTBlipFillProperties blipFill = (CTBlipFillProperties)obj;
- paint = createTexturePaint(blipFill, graphics, parentPart);
- }
- else if (obj instanceof CTGradientFillProperties) {
- CTGradientFillProperties gradFill = (CTGradientFillProperties) obj;
- if (gradFill.isSetLin()) {
- paint = createLinearGradientPaint(gradFill, anchor, theme, phClr);
- } else if (gradFill.isSetPath()){
- CTPathShadeProperties ps = gradFill.getPath();
- if(ps.getPath() == STPathShadeType.CIRCLE){
- paint = createRadialGradientPaint(gradFill, anchor, theme, phClr);
- }
- }
- }
-
- return paint;
- }
-
- private Paint createTexturePaint(CTBlipFillProperties blipFill, Graphics2D graphics,
- PackagePart parentPart){
- Paint paint = null;
- CTBlip blip = blipFill.getBlip();
- String blipId = blip.getEmbed();
- PackageRelationship rel = parentPart.getRelationship(blipId);
- if (rel != null) {
- XSLFImageRendener renderer = null;
- if (graphics != null)
- renderer = (XSLFImageRendener) graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER);
- if (renderer == null) renderer = new XSLFImageRendener();
-
- try {
- BufferedImage img = renderer.readImage(parentPart.getRelatedPart(rel));
- if (blip.sizeOfAlphaModFixArray() > 0) {
- float alpha = blip.getAlphaModFixArray(0).getAmt() / 100000.f;
- AlphaComposite ac = AlphaComposite.getInstance(
- AlphaComposite.SRC_OVER, alpha);
- if (graphics != null) graphics.setComposite(ac);
- }
-
- if(img != null) {
- paint = new TexturePaint(
- img, new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
- }
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- }
- return paint;
- }
-
- private static Paint createLinearGradientPaint(
- CTGradientFillProperties gradFill, Rectangle2D anchor,
- XSLFTheme theme, CTSchemeColor phClr) {
- double angle = gradFill.getLin().getAng() / 60000;
- 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);
- }
- });
-
- Color[] colors = new Color[gs.length];
- float[] fractions = new float[gs.length];
-
- AffineTransform at = AffineTransform.getRotateInstance(
- Math.toRadians(angle),
- anchor.getX() + anchor.getWidth() / 2,
- anchor.getY() + anchor.getHeight() / 2);
-
- double diagonal = Math.sqrt(anchor.getHeight() * anchor.getHeight() + anchor.getWidth() * anchor.getWidth());
- Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth() / 2 - diagonal / 2,
- anchor.getY() + anchor.getHeight() / 2);
- p1 = at.transform(p1, null);
-
- Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight() / 2);
- p2 = at.transform(p2, null);
-
- snapToAnchor(p1, anchor);
- snapToAnchor(p2, anchor);
-
- for (int i = 0; i < gs.length; i++) {
- CTGradientStop stop = gs[i];
- colors[i] = new XSLFColor(stop, theme, phClr).getColor();
- fractions[i] = stop.getPos() / 100000.f;
- }
-
- // Trick to return GradientPaint on JDK 1.5 and LinearGradientPaint on JDK 1.6+
- Paint paint;
- try {
- Class clz = Class.forName("java.awt.LinearGradientPaint");
- Constructor c =
- clz.getConstructor(Point2D.class, Point2D.class, float[].class, Color[].class);
- paint = (Paint) c.newInstance(p1, p2, fractions, colors);
- } catch (ClassNotFoundException e) {
- paint = new GradientPaint(p1, colors[0], p2, colors[colors.length - 1]);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return paint;
- }
-
- private static Paint createRadialGradientPaint(
- CTGradientFillProperties gradFill, Rectangle2D anchor,
- XSLFTheme theme, CTSchemeColor phClr) {
- CTGradientStop[] gs = gradFill.getGsLst().getGsArray();
-
- Point2D pCenter = new Point2D.Double(anchor.getX() + anchor.getWidth()/2,
- anchor.getY() + anchor.getHeight()/2);
-
- float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight());
-
- 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);
- }
- });
-
- Color[] colors = new Color[gs.length];
- float[] fractions = new float[gs.length];
-
-
- for (int i = 0; i < gs.length; i++) {
- CTGradientStop stop = gs[i];
- colors[i] = new XSLFColor(stop, theme, phClr).getColor();
- fractions[i] = stop.getPos() / 100000.f;
- }
-
- // Trick to return GradientPaint on JDK 1.5 and RadialGradientPaint on JDK 1.6+
- Paint paint;
- try {
- Class clz = Class.forName("java.awt.RadialGradientPaint");
- Constructor c =
- clz.getConstructor(Point2D.class, float.class,
- float[].class, Color[].class);
- paint = (Paint) c.newInstance(pCenter, radius, fractions, colors);
- } catch (ClassNotFoundException e) {
- // the result on JDK 1.5 is incorrect, but it is better than nothing
- paint = new GradientPaint(
- new Point2D.Double(anchor.getX(), anchor.getY()),
- colors[0], pCenter, colors[colors.length - 1]);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return paint;
- }
-
- private static void snapToAnchor(Point2D p, Rectangle2D anchor) {
- if (p.getX() < anchor.getX()) {
- p.setLocation(anchor.getX(), p.getY());
- } else if (p.getX() > (anchor.getX() + anchor.getWidth())) {
- p.setLocation(anchor.getX() + anchor.getWidth(), p.getY());
- }
-
- if (p.getY() < anchor.getY()) {
- p.setLocation(p.getX(), anchor.getY());
- } else if (p.getY() > (anchor.getY() + anchor.getHeight())) {
- p.setLocation(p.getX(), anchor.getY() + anchor.getHeight());
- }
- }
-
-
- @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated
- Paint getPaint(Graphics2D graphics, XmlObject spPr, CTSchemeColor phClr) {
-
- Paint paint = null;
- for (XmlObject obj : spPr.selectPath("*")) {
- paint = selectPaint(graphics, obj, phClr, _shape.getSheet().getPackagePart());
- if(paint != null) break;
- }
- return paint == NO_PAINT ? null : paint;
- }
-
-
- /**
- * fetch shape fill as a java.awt.Paint
- *
- * @return either Color or GradientPaint or TexturePaint or null
- */
- Paint getFillPaint(final Graphics2D graphics) {
- PropertyFetcher<Paint> fetcher = new PropertyFetcher<Paint>() {
- public boolean fetch(XSLFSimpleShape shape) {
- CTShapeProperties spPr = shape.getSpPr();
- if (spPr.isSetNoFill()) {
- setValue(RenderableShape.NO_PAINT); // use it as 'nofill' value
- return true;
- }
- Paint paint = getPaint(graphics, spPr, null);
- if (paint != null) {
- setValue(paint);
- return true;
- }
- return false;
- }
- };
- _shape.fetchShapeProperty(fetcher);
-
- Paint paint = fetcher.getValue();
- if (paint == null) {
- // fill color was not found, check if it is defined in the theme
- CTShapeStyle style = _shape.getSpStyle();
- if (style != null) {
- // get a reference to a fill style within the style matrix.
- CTStyleMatrixReference fillRef = style.getFillRef();
- // 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 = _shape.getSheet();
- XSLFTheme theme = sheet.getTheme();
- XmlObject fillProps = null;
- if(idx >= 1 && idx <= 999){
- fillProps = theme.getXmlObject().
- getThemeElements().getFmtScheme().getFillStyleLst().selectPath("*")[idx - 1];
- } else if (idx >= 1001 ){
- fillProps = theme.getXmlObject().
- getThemeElements().getFmtScheme().getBgFillStyleLst().selectPath("*")[idx - 1001];
- }
- if(fillProps != null) {
- paint = selectPaint(graphics, fillProps, phClr, sheet.getPackagePart());
- }
- }
- }
- return paint == RenderableShape.NO_PAINT ? null : paint;
- }
-
- public Paint getLinePaint(final Graphics2D graphics) {
- PropertyFetcher<Paint> fetcher = new PropertyFetcher<Paint>() {
- public boolean fetch(XSLFSimpleShape shape) {
- CTLineProperties spPr = shape.getSpPr().getLn();
- if (spPr != null) {
- if (spPr.isSetNoFill()) {
- setValue(NO_PAINT); // use it as 'nofill' value
- return true;
- }
- Paint paint = getPaint(graphics, spPr, null);
- if (paint != null) {
- setValue(paint);
- return true;
- }
- }
- return false;
-
- }
- };
- _shape.fetchShapeProperty(fetcher);
-
- Paint paint = fetcher.getValue();
- if (paint == null) {
- // line color was not found, check if it is defined in the theme
- CTShapeStyle style = _shape.getSpStyle();
- if (style != null) {
- // get a reference to a line style within the style matrix.
- CTStyleMatrixReference lnRef = style.getLnRef();
- int idx = (int)lnRef.getIdx();
- CTSchemeColor phClr = lnRef.getSchemeClr();
- if(idx > 0){
- XSLFTheme theme = _shape.getSheet().getTheme();
- XmlObject lnProps = theme.getXmlObject().
- getThemeElements().getFmtScheme().getLnStyleLst().selectPath("*")[idx - 1];
- paint = getPaint(graphics, lnProps, phClr);
- }
- }
- }
-
- return paint == NO_PAINT ? null : paint;
- }
-
- /**
- * convert PPT dash into java.awt.BasicStroke
- *
- * The mapping is derived empirically on PowerPoint 2010
- */
- private static float[] getDashPattern(LineDash lineDash, float lineWidth) {
- float[] dash = null;
- switch (lineDash) {
- case SYS_DOT:
- dash = new float[]{lineWidth, lineWidth};
- break;
- case SYS_DASH:
- dash = new float[]{2 * lineWidth, 2 * lineWidth};
- break;
- case DASH:
- dash = new float[]{3 * lineWidth, 4 * lineWidth};
- break;
- case DASH_DOT:
- dash = new float[]{4 * lineWidth, 3 * lineWidth, lineWidth,
- 3 * lineWidth};
- break;
- case LG_DASH:
- dash = new float[]{8 * lineWidth, 3 * lineWidth};
- break;
- case LG_DASH_DOT:
- dash = new float[]{8 * lineWidth, 3 * lineWidth, lineWidth,
- 3 * lineWidth};
- break;
- case LG_DASH_DOT_DOT:
- dash = new float[]{8 * lineWidth, 3 * lineWidth, lineWidth,
- 3 * lineWidth, lineWidth, 3 * lineWidth};
- break;
- }
- return dash;
- }
-
-
- public Stroke applyStroke(Graphics2D graphics) {
-
- float lineWidth = (float) _shape.getLineWidth();
- LineDash lineDash = _shape.getLineDash();
- float[] dash = null;
- float dash_phase = 0;
- if (lineDash != null) {
- dash = getDashPattern(lineDash, lineWidth);
- }
-
- int cap = BasicStroke.CAP_BUTT;
- LineCap lineCap = _shape.getLineCap();
- if (lineCap != null) {
- switch (lineCap) {
- case ROUND:
- cap = BasicStroke.CAP_ROUND;
- break;
- case SQUARE:
- cap = BasicStroke.CAP_SQUARE;
- break;
- default:
- cap = BasicStroke.CAP_BUTT;
- break;
- }
- }
-
- int meter = BasicStroke.JOIN_ROUND;
-
- Stroke stroke = new BasicStroke(lineWidth, cap, meter, Math.max(1, lineWidth), dash,
- dash_phase);
- graphics.setStroke(stroke);
- return stroke;
- }
-
- public void render(Graphics2D graphics){
- Collection<Outline> elems = computeOutlines();
-
- // shadow
- XSLFShadow shadow = _shape.getShadow();
-
- // first fill
- Paint fill = getFillPaint(graphics);
- if(fill != null) for(Outline o : elems){
- if(o.getPath().isFilled()){
- if(shadow != null) shadow.fill(graphics, o.getOutline());
-
- graphics.setPaint(fill);
- graphics.fill(o.getOutline());
- }
- }
-
- // then draw any content within this shape (text, image, etc.)
- _shape.drawContent(graphics);
-
- // then stroke the shape outline
- Paint line = getLinePaint(graphics);
- if(line != null) for(Outline o : elems){
- if(o.getPath().isStroked()){
- applyStroke(graphics); // the stroke applies both to the shadow and the shape
-
- if(shadow != null) shadow.draw(graphics, o.getOutline());
-
- graphics.setPaint(line);
- graphics.draw(o.getOutline());
- }
- }
- }
-
- private Collection<Outline> computeOutlines() {
- CustomGeometry geom = _shape.getGeometry();
-
- Collection<Outline> lst = new ArrayList<Outline>();
-
- Rectangle2D anchor = _shape.getAnchor();
- for (Path p : geom) {
-
- double w = p.getW() == -1 ? anchor.getWidth() * Units.EMU_PER_POINT : p.getW();
- double h = p.getH() == -1 ? anchor.getHeight() * Units.EMU_PER_POINT : p.getH();
-
- // the guides in the shape definitions are all defined relative to each other,
- // so we build the path starting from (0,0).
- final Rectangle2D pathAnchor = new Rectangle2D.Double(
- 0,
- 0,
- w,
- h
- );
-
- Context ctx = new Context(geom, pathAnchor, new IAdjustableShape() {
-
- public Guide getAdjustValue(String name) {
- CTPresetGeometry2D prst = _shape.getSpPr().getPrstGeom();
- if (prst.isSetAvLst()) {
- for (CTGeomGuide g : prst.getAvLst().getGdList()) {
- if (g.getName().equals(name)) {
- return new Guide(g);
- }
- }
- }
- return null;
- }
- }) ;
-
- Shape gp = p.getPath(ctx);
-
- // translate the result to the canvas coordinates in points
- AffineTransform at = new AffineTransform();
- at.translate(anchor.getX(), anchor.getY());
-
- double scaleX, scaleY;
- if (p.getW() != -1) {
- scaleX = anchor.getWidth() / p.getW();
- } else {
- scaleX = 1.0 / Units.EMU_PER_POINT;
- }
- if (p.getH() != -1) {
- scaleY = anchor.getHeight() / p.getH();
- } else {
- scaleY = 1.0 / Units.EMU_PER_POINT;
- }
-
- at.scale(scaleX, scaleY);
-
- Shape canvasShape = at.createTransformedShape(gp);
-
- lst.add(new Outline(canvasShape, p));
- }
-
- // add any shape-specific stuff here (line decorations, etc.)
- lst.addAll(_shape.getCustomOutlines());
- return lst;
- }
-
- }
|