123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /* ====================================================================
- 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.BasicStroke;
- import java.awt.Graphics2D;
- import java.awt.geom.AffineTransform;
- import java.awt.geom.Rectangle2D;
- import java.util.Locale;
-
- import org.apache.poi.sl.usermodel.PlaceableShape;
- import org.apache.poi.sl.usermodel.Shape;
- import org.apache.poi.sl.usermodel.StrokeStyle;
- import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;
- import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;
-
-
- public class DrawShape implements Drawable {
-
- protected final Shape<?,?> shape;
-
- public DrawShape(Shape<?,?> shape) {
- this.shape = shape;
- }
-
- /**
- * Sometimes it's necessary to distinguish between XSLF/HSLF for the rendering.
- * Use this method on the shape to determine, if we work on the BIFF implementation
- *
- * @param shape the shape to render
- * @return {@code true} if HSLF implementation is used
- */
- protected static boolean isHSLF(Shape<?,?> shape) {
- return shape.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf");
- }
-
- /**
- * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
- *
- * @param graphics the graphics whos transform matrix will be modified
- */
- @Override
- public void applyTransform(Graphics2D graphics) {
- if (!(shape instanceof PlaceableShape)) {
- return;
- }
-
- PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape;
- final boolean isHSLF = isHSLF(shape);
- AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
- if (tx == null) {
- tx = new AffineTransform();
- }
-
- // we saw one document failing here, probably the format is slightly broken, but
- // maybe better to try to handle it more gracefully
- java.awt.Shape transformedShape = tx.createTransformedShape(ps.getAnchor());
- if(transformedShape == null) {
- return;
- }
-
- final Rectangle2D anchor = transformedShape.getBounds2D();
-
- char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' };
- for (char ch : cmds) {
- switch (ch) {
- case 'h':
- //flip horizontal
- if (ps.getFlipHorizontal()) {
- graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
- graphics.scale(-1, 1);
- graphics.translate(-anchor.getX(), -anchor.getY());
- }
- break;
- case 'v':
- //flip vertical
- if (ps.getFlipVertical()) {
- graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
- graphics.scale(1, -1);
- graphics.translate(-anchor.getX(), -anchor.getY());
- }
- break;
- case 'r':
- // rotation
- double rotation = ps.getRotation();
- if (rotation != 0.) {
- // PowerPoint rotates shapes relative to the geometric center
- double centerX = anchor.getCenterX();
- double centerY = anchor.getCenterY();
-
- // normalize rotation
- rotation %= 360.;
- if (rotation < 0) {
- rotation += 360.;
- }
-
- int quadrant = (((int)rotation+45)/90)%4;
- double scaleX = 1.0, scaleY = 1.0;
-
- // scale to bounding box (bug #53176)
- if (quadrant == 1 || quadrant == 3) {
- // In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation
- // (45-135 degrees and 225-315 degrees), we need to first rotate the shape by a multiple
- // of 90 degrees and then resize the bounding box to its original bbox. After that we can
- // rotate the shape to the exact rotation amount.
- // It's strange that you'll need to rotate the shape back and forth again, but you can
- // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might
- // be already (differently) scaled, so you can paint the shape in its default orientation
- // and later on, turn it around again to compare it with its original size ...
-
- AffineTransform txs;
- if (isHSLF) {
- txs = new AffineTransform(tx);
- } else {
- // this handling is only based on try and error ... not sure why xslf is handled differently.
- txs = new AffineTransform();
- txs.translate(centerX, centerY);
- txs.rotate(Math.PI/2.); // actually doesn't matter if +/- 90 degrees
- txs.translate(-centerX, -centerY);
- txs.concatenate(tx);
- }
-
- txs.translate(centerX, centerY);
- txs.rotate(Math.PI/2.);
- txs.translate(-centerX, -centerY);
-
- Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();
-
- scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());
- scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());
- } else {
- quadrant = 0;
- }
-
- // transformation is applied reversed ...
- graphics.translate(centerX, centerY);
- double rot = Math.toRadians(rotation-quadrant*90.);
- if (rot != 0) {
- graphics.rotate(rot);
- }
- graphics.scale(scaleX, scaleY);
- rot = Math.toRadians(quadrant*90.);
- if (rot != 0) {
- graphics.rotate(rot);
- }
- graphics.translate(-centerX, -centerY);
- }
- break;
- default:
- throw new RuntimeException("unexpected transform code " + ch);
- }
- }
- }
-
- private static double safeScale(double dim1, double dim2) {
- if (dim1 == 0.) {
- return 1;
- }
- return (dim2 == 0.) ? 1 : dim1/dim2;
- }
-
- @Override
- public void draw(Graphics2D graphics) {
- }
-
- @Override
- public void drawContent(Graphics2D graphics) {
- }
-
- public static Rectangle2D getAnchor(Graphics2D graphics, PlaceableShape<?,?> shape) {
- return getAnchor(graphics, shape.getAnchor());
- }
-
- public static Rectangle2D getAnchor(Graphics2D graphics, Rectangle2D anchor) {
- if(graphics == null) {
- return anchor;
- }
-
- AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
- if(tx != null && !tx.isIdentity() && tx.createTransformedShape(anchor) != null) {
- anchor = tx.createTransformedShape(anchor).getBounds2D();
- }
- return anchor;
- }
-
- protected Shape<?,?> getShape() {
- return shape;
- }
-
- protected static BasicStroke getStroke(StrokeStyle strokeStyle) {
- float lineWidth = (float) strokeStyle.getLineWidth();
- if (lineWidth == 0.0f) {
- // Both PowerPoint and OOo draw zero-length lines as 0.25pt
- lineWidth = 0.25f;
- }
-
- LineDash lineDash = strokeStyle.getLineDash();
- if (lineDash == null) {
- lineDash = LineDash.SOLID;
- }
-
- int dashPatI[] = lineDash.pattern;
- final float dash_phase = 0;
- float[] dashPatF = null;
- if (dashPatI != null) {
- dashPatF = new float[dashPatI.length];
- for (int i=0; i<dashPatI.length; i++) {
- dashPatF[i] = dashPatI[i]*Math.max(1, lineWidth);
- }
- }
-
- LineCap lineCapE = strokeStyle.getLineCap();
- if (lineCapE == null) {
- lineCapE = LineCap.FLAT;
- }
- int lineCap;
- switch (lineCapE) {
- case ROUND:
- lineCap = BasicStroke.CAP_ROUND;
- break;
- case SQUARE:
- lineCap = BasicStroke.CAP_SQUARE;
- break;
- default:
- case FLAT:
- lineCap = BasicStroke.CAP_BUTT;
- break;
- }
-
- int lineJoin = BasicStroke.JOIN_ROUND;
-
- return new BasicStroke(lineWidth, lineCap, lineJoin, lineWidth, dashPatF, dash_phase);
- }
- }
|