/******************************************************************************* * Copyright (c) 2009, Adobe Systems Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Adobe Systems Incorporated nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ package com.adobe.dp.office.vml; import java.util.Iterator; import java.util.Vector; import com.adobe.dp.office.word.Element; public class VMLPathConverter implements VMLEnv { float xscale; float yscale; int[] adj; VMLCoordPair origin; VMLCoordPair size; VMLCoordPair limo; VMLShapeElement shape; VMLShapeTypeElement shapeType; Vector callouts = new Vector(); float outerWidth = 100; float outerHeight = 100; float pathScaleX = 1; float pathScaleY = 1; float pathScale = 1; public VMLPathConverter(VMLShapeElement shape) { this.shape = shape; this.shapeType = shape.type; if (this.shapeType == null) return; this.adj = (shape.adj == null ? shapeType.adj : shape.adj); this.origin = (shape.origin == null ? shapeType.origin : shape.origin); this.size = (shape.size == null ? shapeType.size : shape.size); this.limo = (shape.limo == null ? shapeType.limo : shape.limo); } public void setScale(float xscale, float yscale) { this.xscale = xscale; this.yscale = yscale; } public int resolveCallout(VMLCallout callout) { if (callout.code == '#') return adj[callout.index]; return ((Integer) callouts.get(callout.index)).intValue(); } public int resolveEnv(String env) { env = env.toLowerCase(); if (env.equals("width")) return size.x; if (env.equals("height")) return size.y; if (env.equals("xcenter")) return origin.x + size.x / 2; if (env.equals("ycenter")) return origin.y + size.y / 2; if (env.equals("xlimo")) return (limo == null ? 0 : limo.x); if (env.equals("ylimo")) return (limo == null ? 0 : limo.y); if (env.equals("linedrawn")) return shape.stroke == null ? 0 : 1; if (env.equals("pixellinewidth")) { String weight = shape.strokeWeight; if (weight != null) { float w = readCSSLength(weight, 0); if (w > 0) return (int) Math.round(w * xscale * pathScale); } } if (env.equals("pixelwidth")) { return (int) Math.round(size.x * xscale * pathScale); } if (env.equals("pixelheight")) { return (int) Math.round(size.x * xscale * pathScale); } throw new RuntimeException("unknown env: " + env); } public void readFormulas() { Element formulas = null; Iterator it = shapeType.content(); while (it.hasNext()) { Object n = it.next(); if (n instanceof VMLFormulasElement) { formulas = (VMLFormulasElement) n; break; } } if (formulas == null) return; it = formulas.content(); while (it.hasNext()) { Object n = it.next(); if (n instanceof VMLFElement) { VMLFElement f = (VMLFElement) n; int val = f.eval(this); callouts.add(new Integer(val)); } } } public VMLCoordPair getSize() { return size; } public VMLCoordPair getOrigin() { return origin; } public void setOuterSize(float width, float height) { outerWidth = width; outerHeight = height; pathScaleX = width / size.x; pathScaleY = height / size.y; pathScale = (pathScaleX > pathScaleY ? pathScaleY : pathScaleX); } private float transformX(int x) { float r; if (limo != null) { if (x < limo.x) r = -outerWidth / 2 + pathScale * (x - origin.x); else r = outerWidth / 2 - pathScale * (origin.x + size.x - x); } else { r = pathScaleX * (x - origin.x) - outerWidth / 2; } return Math.round(r * 100) / 100.0f; } private float transformY(int y) { float r; if (limo != null) { if (y < limo.y) r = -outerHeight / 2 + pathScale * (y - origin.y); else r = outerHeight / 2 - pathScale * (origin.y + size.y - y); } else { r = pathScaleY * (y - origin.y) - outerHeight / 2; } return Math.round(r * 100) / 100.0f; } private int resolve(Object arg) { if (arg instanceof VMLCallout) { return resolveCallout((VMLCallout) arg); } else if (arg instanceof Integer) { return ((Integer) arg).intValue(); } throw new RuntimeException("bad arg: " + arg); } private void drawArrow(StringBuffer sb, String type, int sx, int sy, int tx, int ty) { double x0 = transformX(sx); double y0 = transformY(sy); double x1 = transformX(tx); double y1 = transformY(ty); double dx = x0 - x1; double dy = y0 - y1; double len = Math.sqrt(dx * dx + dy * dy); if (len < 0.001) return; double nx = dx / len; double ny = dy / len; double targetLen = 100; if (type.equals("diamond") || type.equals("oval")) { double targetHalfWidth = targetLen / 3; double ax1 = x1 + targetHalfWidth * nx; double ay1 = y1 + targetHalfWidth * ny; double ax2 = x1 - targetHalfWidth * ny; double ay2 = y1 + targetHalfWidth * nx; double ax3 = x1 - targetHalfWidth * nx; double ay3 = y1 - targetHalfWidth * ny; double ax4 = x1 + targetHalfWidth * ny; double ay4 = y1 - targetHalfWidth * nx; sb.append('M'); sb.append(Math.round(ax1 * 100) / 100.0); sb.append(' '); sb.append(Math.round(ay1 * 100) / 100.0); sb.append('L'); sb.append(Math.round(ax2 * 100) / 100.0); sb.append(' '); sb.append(Math.round(ay2 * 100) / 100.0); sb.append('L'); sb.append(Math.round(ax3 * 100) / 100.0); sb.append(' '); sb.append(Math.round(ay3 * 100) / 100.0); sb.append('L'); sb.append(Math.round(ax4 * 100) / 100.0); sb.append(' '); sb.append(Math.round(ay4 * 100) / 100.0); sb.append('z'); } else { double targetHalfWidth = targetLen / 4; double bx = x1 + targetLen * nx; double by = y1 + targetLen * ny; double ax1 = bx + targetHalfWidth * ny; double ay1 = by - targetHalfWidth * nx; double ax2 = bx - targetHalfWidth * ny; double ay2 = by + targetHalfWidth * nx; sb.append('M'); sb.append(Math.round(ax1 * 100) / 100.0); sb.append(' '); sb.append(Math.round(ay1 * 100) / 100.0); sb.append('L'); sb.append(x1); sb.append(' '); sb.append(y1); sb.append('L'); sb.append(Math.round(ax2 * 100) / 100.0); sb.append(' '); sb.append(Math.round(ay2 * 100) / 100.0); sb.append('z'); } } private void startLine(StringBuffer sb, int x0, int y0, int x1, int y1) { if (shape.startArrow != null && !shape.startArrow.equals("none")) { drawArrow(sb, shape.startArrow, x1, y1, x0, y0); } sb.append('M'); sb.append(transformX(x0)); sb.append(' '); sb.append(transformY(y0)); } private void endLine(StringBuffer sb, int x0, int y0, int x1, int y1) { if (shape.endArrow != null && !shape.endArrow.equals("none")) { drawArrow(sb, shape.endArrow, x0, y0, x1, y1); } } public String getSVGPath() { VMLPathSegment[] segments = shapeType.path; if (segments == null) return null; StringBuffer sb = new StringBuffer(); int lastX = 0; int lastY = 0; int prevX = 0; int prevY = 0; boolean movedTo = false; for (int i = 0; i < segments.length; i++) { VMLPathSegment s = segments[i]; if (s.command.equals("m")) { lastX = resolve(s.args[0]); lastY = resolve(s.args[1]); movedTo = false; } else if (s.command.equals("l")) { // lineto for (int k = 0; k + 1 < s.args.length; k += 2) { int x = resolve(s.args[k]); int y = resolve(s.args[k + 1]); if (!movedTo) { startLine(sb, lastX, lastY, x, y); movedTo = true; } prevX = lastX; prevY = lastY; lastX = x; lastY = y; sb.append('L'); sb.append(transformX(lastX)); sb.append(' '); sb.append(transformY(lastY)); } } else if (s.command.equals("t")) { // rmoveto lastX += resolve(s.args[0]); lastY += resolve(s.args[1]); movedTo = false; } else if (s.command.equals("r")) { // rlineto int x0 = lastX; int y0 = lastY; for (int k = 0; k + 1 < s.args.length; k += 2) { if (k > 0) sb.append(' '); int x = resolve(s.args[k]) + x0; int y = resolve(s.args[k + 1]) + y0; if (!movedTo) { startLine(sb, lastX, lastY, x, y); movedTo = true; } prevX = lastX; prevY = lastY; lastX = x; lastY = y; sb.append('L'); sb.append(transformX(lastX)); sb.append(' '); sb.append(transformY(lastY)); } } else if (s.command.equals("x")) { if (movedTo) { sb.append('z'); movedTo = false; } } else if (s.command.equals("e")) { if (movedTo) { endLine(sb, prevX, prevY, lastX, lastY); // no such SVG thing movedTo = false; } } else if (s.command.equals("qy") || s.command.equals("qx")) { boolean tanX = s.command.equals("qx"); for (int k = 0; k + 1 < s.args.length; k += 2) { int tx = resolve(s.args[k]); int ty = resolve(s.args[k + 1]); if (!movedTo) { if (tanX) startLine(sb, lastX, lastY, tx, lastY); else startLine(sb, lastX, lastY, lastX, ty); movedTo = true; } sb.append('C'); if (tanX) { sb.append(transformX(lastX + 2 * (tx - lastX) / 3)); sb.append(' '); sb.append(transformY(lastY)); sb.append(' '); sb.append(transformX(tx)); sb.append(' '); sb.append(transformY(lastY + (ty - lastY) / 3)); prevX = tx; prevY = lastY; } else { sb.append(transformX(lastX)); sb.append(' '); sb.append(transformY(lastY + 2 * (ty - lastY) / 3)); sb.append(' '); sb.append(transformX(lastX + (tx - lastX) / 3)); sb.append(' '); sb.append(transformY(ty)); prevX = lastX; prevY = ty; } sb.append(' '); sb.append(transformX(tx)); sb.append(' '); sb.append(transformY(ty)); lastX = tx; lastY = ty; tanX = !tanX; } } else if (s.command.equals("c")) { for (int k = 0; k + 5 < s.args.length; k += 6) { int cx1 = resolve(s.args[k]); int cy1 = resolve(s.args[k + 1]); int cx2 = resolve(s.args[k + 2]); int cy2 = resolve(s.args[k + 3]); int cx3 = resolve(s.args[k + 4]); int cy3 = resolve(s.args[k + 5]); if (!movedTo) { startLine(sb, lastX, lastY, cx1, cy1); movedTo = true; } sb.append('C'); sb.append(transformX(cx1)); sb.append(' '); sb.append(transformY(cy1)); sb.append(' '); sb.append(transformX(cx2)); sb.append(' '); sb.append(transformY(cy2)); sb.append(' '); sb.append(transformX(cx3)); sb.append(' '); sb.append(transformY(cy3)); prevX = cx2; prevY = cy2; lastX = cx3; lastY = cy3; } } else { System.err.println("path command " + s.command + " not supported"); } } return sb.toString(); } static public float readCSSLength(String len, float def) { int ulen = 2; float conv = 0; if (len.endsWith("pt") || len.endsWith("px")) { conv = 1; } else if (len.endsWith("in")) { conv = 1 / 72.0f; } if (conv > 0) { try { return Float.parseFloat(len.substring(0, len.length() - ulen)) * conv; } catch (Exception e) { e.printStackTrace(); } } return def; } public float[] getTextBox() { Object[] textbox = shapeType.textbox; if (textbox == null) return null; float[] tb = new float[textbox.length]; for (int i = 0; i < textbox.length; i += 2) { tb[i] = transformX(resolve(textbox[i])); tb[i + 1] = transformY(resolve(textbox[i + 1])); } return tb; } }