/**************************************************************************** * Copyright (c) 2008, 2009 Jeremy Dowdall * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation *****************************************************************************/ package org.eclipse.nebula.cwt.svg; import java.util.HashMap; import java.util.Map; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; /** * An svg document can contain one or more svg fragments, each denoted * by the "svg" tag. Each of these consists of all the information * necessary to paint a graphic to the screen, including definition * and css style child elements. * <p>Of particular importance is that the svg fragment can also contain * a viewbox which can be used for scaling the image to a particular size. * Therefore this element is where the real-world rendered dimensions * interact with the svg dimenions.</p> * <p>See also: * <a href="http://www.w3.org/TR/SVG/struct.html#SVGElement">http://www.w3.org/TR/SVG/struct.html#SVGElement</a></p> */ public class SvgFragment extends SvgContainer { Float x; Float y; Float width; Float height; /** * viewBox[0] == x * viewBox[1] == y * viewBox[2] == w * viewBox[3] == h */ float[] viewBox; SvgTransform boundsTransform; boolean preserveAspectRatio; private Map<String, SvgElement> elementMap; SvgFragment(SvgContainer container, String id) { super(container, id); elementMap = new HashMap<String, SvgElement>(); boundsTransform = new SvgTransform(); boundsTransform.data = new float[] { 1, 0, 0, 1, 0, 0 }; } /** * Apply this svg fragment to the given graphics context, scaled to fit within * the given bounds. This method will recursive call the apply methods of all * contained svg elements, thereby painting the entire fragment to the given * graphics context. * @param gc the graphics context * @param bounds the bounds to which this fragment will be scaled */ public void apply(GC gc, Rectangle bounds) { boundsTransform.translate(bounds.x, bounds.y); if(viewBox != null) { float sx = bounds.width / viewBox[2]; float sy = bounds.height / viewBox[3]; boundsTransform.scale(sx, sy); } else if(width != null && height != null){ float sx = bounds.width / width; float sy = bounds.height / height; boundsTransform.scale(sx, sy); } super.apply(gc); boundsTransform.data = new float[] { 1, 0, 0, 1, 0, 0 }; } public SvgElement getElement(String id) { return elementMap.get(id); } @Override public SvgFragment getFragment() { return this; } /** * Return a map of css styles for the given class name, if it exists. * <p>Each SvgFragment can contain a style element which consists of css styles.</p> * @param className the name of the css class to return styles for. * @return a map of css style for the given class name if it exists, null otherwise. */ public Map<String, String> getStyles(String className) { SvgElement element = elementMap.get("style"); //$NON-NLS-1$ if(element instanceof SvgStyle) { Map<String, Map<String, String>> classes = ((SvgStyle) element).styles; if(classes != null) { return classes.get(className); } } return null; } @Override public float[] getViewport() { if(x == null || y == null) { return new float[] { 0, 0, width, height }; } else { return new float[] { x, y, width, height }; } } /** * Returns true if this fragment contains an SvgElement with the given id. * @param id the id of the element * @return true if the element exists, false otherwise */ public boolean hasElement(String id) { return elementMap.containsKey(id); } /** * Returns true if this SvgFragment is at the outermost level, meaning it * is a direct child of the SvgDocument. This is an important distinction * because, as with all svg elements, fragments can be nested. Each svg * fragment will establish a new coordinate system, but only the outer * fragment will determine the scaling necessary to display at the requested size. * @return true if this fragment is at the outermost level, false otherwise. */ public boolean isOutermost() { return getContainer() == null; } void put(SvgElement element) { String id = element.getId(); if(id != null) { elementMap.put(id, element); } } }