All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.blaisemath.svg.SVGElements Maven / Gradle / Ivy

/**
 * SVGObjectFactory.java
 * Created on Sep 26, 2014
 */
package com.googlecode.blaisemath.svg;

/*
 * #%L
 * BlaiseSVG
 * --
 * Copyright (C) 2014 - 2017 Elisha Peterson
 * --
 * Licensed 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.
 * #L%
 */

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.googlecode.blaisemath.graphics.swing.WrappedTextRenderer;
import com.googlecode.blaisemath.style.Anchor;
import com.googlecode.blaisemath.style.AttributeSet;
import com.googlecode.blaisemath.style.Marker;
import com.googlecode.blaisemath.style.Markers;
import com.googlecode.blaisemath.style.Renderer;
import com.googlecode.blaisemath.style.Styles;
import com.googlecode.blaisemath.util.AnchoredIcon;
import com.googlecode.blaisemath.util.AnchoredImage;
import com.googlecode.blaisemath.util.AnchoredText;
import com.googlecode.blaisemath.util.Images;
import com.googlecode.blaisemath.util.OrientedPoint2D;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.swing.Icon;

/**
 * Factory methods for converting to/from SVG Objects.
 * @author petereb1
 */
public class SVGElements {
    private static final Logger LOG = Logger.getLogger(SVGElements.class.getName());
    
    //
    // UTILITY CLASS - PREVENT INSTANTIATION
    //
    private SVGElements() {
    }

    /**
     * Create new svg object from given id, shape, and style. Supports shapes
     * {@link Rectangle2D}, {@link RoundRectangle2D}, {@link Ellipse2D}, and
     * {@link Line2D}.
     * @param id object id
     * @param shape object's shape
     * @param style object's style
     * @return svg object
     */
    public static SVGElement create(String id, Shape shape, AttributeSet style) {
        SVGElement res;
        if (shape instanceof Rectangle2D || shape instanceof RoundRectangle2D) {
            res = SVGRectangle.shapeConverter().reverse().convert((RectangularShape) shape);
        } else if (shape instanceof Ellipse2D) {
            Ellipse2D ell = (Ellipse2D) shape;
            if (ell.getWidth() == ell.getHeight()) {
                res = SVGCircle.shapeConverter().reverse().convert(ell);
            } else {
                res = SVGEllipse.shapeConverter().reverse().convert(ell);
            }
        } else if (shape instanceof Line2D) {
            res = SVGLine.shapeConverter().reverse().convert((Line2D) shape);
        } else if (shape instanceof Path2D) {
            res = SVGPath.shapeConverter().reverse().convert((Path2D) shape);
        } else if (shape instanceof Area) {
            res = SVGPath.create(shape.getPathIterator(null));
        } else {
            Logger.getLogger(SVGElements.class.getName()).log(Level.WARNING, "Shapes of type {0} are not yet supported.", shape.getClass());
            return null;
        }
        res.setId(id);
        res.setStyle(AttributeSet.create(style.getAttributeMap()));
        if (style.get(Styles.FILL) == null) {
            res.getStyle().put(Styles.FILL, null);
        }
        if (style.get(Styles.STROKE) == null) {
            res.getStyle().put(Styles.STROKE, null);
        }
        return res;
    }

    /**
     * Create new svg object from a provided point/style.
     * @param id object id
     * @param point the point
     * @param style object's style
     * @return svg object
     */
    public static SVGElement create(String id, Point2D point, AttributeSet style) {
        Marker m = (Marker) style.get(Styles.MARKER);
        if (m == null) {
            m = Markers.CIRCLE;
        }
        Float ort = style.getFloat(Styles.MARKER_ORIENT, point instanceof OrientedPoint2D ? (float) ((OrientedPoint2D)point).getAngle() : 0f);
        Float rad = style.getFloat(Styles.MARKER_RADIUS, 4f);
        Shape shape = m.create(point, ort, rad);
        return create(id, shape, style);
    }

    /**
     * Create new svg text object from given id, anchored text, and style.
     * @param id object id
     * @param text object text
     * @param style object's style
     * @param rend the text renderer
     * @return svg text object
     */
    public static SVGElement create(String id, AnchoredText text, AttributeSet style, @Nullable Renderer rend) {
        if (rend instanceof WrappedTextRenderer) {
            SVGElement res = createWrappedText(text.getText(), style, ((WrappedTextRenderer) rend).getTextBounds());
            res.setId(id);
            return res;
        }
        SVGText res = SVGText.textConverter().reverse().convert(text);
        res.setId(id);
        Object ta = style.get(Styles.TEXT_ANCHOR);
        AttributeSet copy = AttributeSet.create(style.getAttributeMap());
        if (ta instanceof Anchor) {
            copy.put(Styles.TEXT_ANCHOR, Styles.toTextAnchor((Anchor) ta));
            copy.put(Styles.ALIGN_BASELINE, Styles.toAlignBaseline((Anchor) ta));
        } else if (ta instanceof String && Styles.isAnchorName(ta)) {
            copy.put(Styles.TEXT_ANCHOR, Styles.toTextAnchor((String) ta));
            copy.put(Styles.ALIGN_BASELINE, Styles.toAlignBaseline((String) ta));
        }
        res.setStyle(copy);
        return res;
    }

    /** 
     * Generates group of text elements formed by wrapping text 
     * @param text text
     * @param style text style
     * @param bounds text bounds
     * @return group element with several lines (or text element if a single line)
     */
    public static SVGElement createWrappedText(String text, AttributeSet style, RectangularShape bounds) {
        Graphics2D testCanvas = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB).createGraphics();
        Iterable lines = WrappedTextRenderer.computeLines(text, style, bounds, WrappedTextRenderer.defaultInsets(), testCanvas);
        
        SVGGroup grp = new SVGGroup();
        for (WrappedTextRenderer.StyledText st : lines) {
            grp.addElement(create(null, st.getText(), st.getStyle(), null));
        }
        
        SVGElement res = grp.getElements().size() == 1 ? grp.getElements().get(0) : grp;
        res.setStyle(AttributeSet.create(style.getAttributeMap()));
        return res;
    }

    /**
     * Create new svg image object from given id, anchored image, and style.
     * @param id object id
     * @param img image object
     * @param style object's style
     * @return svg image object
     */
    public static SVGImage create(String id, AnchoredImage img, AttributeSet style) {
        Anchor anchor = Styles.anchorOf(style, Anchor.NORTHWEST);
        Point2D offset = anchor.getRectOffset(img.getWidth(), img.getHeight());
        AnchoredImage adjustedImage = new AnchoredImage(
                img.getX() + offset.getX(), 
                img.getY() - img.getHeight()+ offset.getY(), 
                (double) img.getWidth(), (double) img.getHeight(),
                img.getOriginalImage(), img.getReference());
        SVGImage res = SVGImage.imageConverter().reverse().convert(adjustedImage);
        res.setId(id);
        res.setStyle(AttributeSet.create(style.getAttributeMap()));
        return res;
    }

    /**
     * Create new svg image object from given id, anchored icon, and style.
     * @param id object id
     * @param icon icon object
     * @param style object's style
     * @return svg image object
     */
    public static SVGImage create(String id, AnchoredIcon icon, AttributeSet style) {
        Anchor anchor = Styles.anchorOf(style, Anchor.NORTHWEST);
        Point2D offset = anchor.getRectOffset(icon.getIconWidth(), icon.getIconHeight());
        SVGImage res = new SVGImage(
                icon.getX() + offset.getX(), 
                icon.getY() - icon.getIconHeight() + offset.getY(), 
                (double) icon.getIconWidth(), (double) icon.getIconHeight(), 
                encodeAsUri(icon.getIcon()));
        res.setId(id);
        AttributeSet sty = AttributeSet.create(style.getAttributeMap());
        sty.remove(Styles.TEXT_ANCHOR);
        sty.remove(Styles.ALIGN_BASELINE);
        res.setStyle(sty);
        return res;        
    }
    
    private static String encodeAsUri(Icon icon) {
        try {
            BufferedImage bi = new BufferedImage(2*icon.getIconWidth(), 2*icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = bi.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setTransform(AffineTransform.getScaleInstance(2, 2));
            icon.paintIcon(null, g2, 0, 0);
            return Images.encodeDataUriBase64(bi, "png");
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, "Encoding error", ex);
            return "";
        }
    }

    /**
     * Return true if element is a path type (i.e. no fill expected)
     * @param el an element
     * @return true if its a path
     */
    public static boolean isPath(SVGElement el) {
        return el instanceof SVGLine || el instanceof SVGPolyline;
    }
    
    /** 
     * Return an iterator for an element. Will iterate over this element, and if
     * a group, all nested children of this element. Groups are added before their
     * children.
     * @param element the element to start with
     * @return iteration over this element, and all of its child elements
     */
    public static Iterable shapeIterator(SVGElement element) {
        List res = Lists.newArrayList();
        res.add(element);
        if (element instanceof SVGGroup) {
            for (SVGElement el : ((SVGGroup)element).getElements()) {
                Iterables.addAll(res, shapeIterator(el));
            }
        }
        return res;
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy