/******************************************************************************* * 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.conv; import java.io.PrintWriter; import java.util.Hashtable; import java.util.Iterator; import com.adobe.dp.epub.opf.OPSResource; import com.adobe.dp.epub.opf.Publication; import com.adobe.dp.epub.opf.StyleResource; import com.adobe.dp.epub.ops.OPSDocument; import com.adobe.dp.epub.ops.SVGElement; import com.adobe.dp.office.types.RGBColor; import com.adobe.dp.office.vml.VMLCoordPair; import com.adobe.dp.office.vml.VMLElement; import com.adobe.dp.office.vml.VMLGroupElement; import com.adobe.dp.office.vml.VMLLineElement; import com.adobe.dp.office.vml.VMLOvalElement; import com.adobe.dp.office.vml.VMLPathConverter; import com.adobe.dp.office.vml.VMLRectElement; import com.adobe.dp.office.vml.VMLShadow; import com.adobe.dp.office.vml.VMLShapeElement; import com.adobe.dp.office.word.TXBXContentElement; public class VMLConverter { private Publication epub; private OPSDocument chapter; private OPSResource resource; private WordMLConverter wordConverter; private StyleConverter styleConverter; private boolean embedded; PrintWriter log; VMLConverter(WordMLConverter wordConverter, boolean embedded) { this.wordConverter = wordConverter; this.log = wordConverter.log; this.embedded = embedded; if (!embedded) { epub = wordConverter.getPublication(); styleConverter = new StyleConverter(true); } } void convertVML(OPSResource resource, SVGElement svg, VMLGroupElement group) { Hashtable style = group.getStyle(); if (style == null) return; this.resource = resource; this.chapter = resource.getDocument(); String widthStr = (String) style.get("width"); String heightStr = (String) style.get("height"); VMLCoordPair origin = group.getOrigin(); VMLCoordPair size = group.getSize(); float widthPt = VMLPathConverter.readCSSLength(widthStr, 100); float heightPt = VMLPathConverter.readCSSLength(heightStr, 100); if (!embedded) { StyleResource global = (StyleResource) epub.getResourceByName("OPS/global.css"); chapter.addStyleResource(global); svg.setAttribute("width", Float.toString(widthPt)); svg.setAttribute("height", Float.toString(heightPt)); } float scaleX = size.x / widthPt; float scaleY = size.y / heightPt; svg.setAttribute("viewBox", origin.x + " " + origin.y + " " + size.x + " " + size.y); convertVMLChildren(svg, group, scaleX, scaleY); } private void convertVMLChildren(SVGElement svg, VMLElement vml, float scaleX, float scaleY) { Iterator it = vml.content(); while (it.hasNext()) { Object child = it.next(); if (child instanceof VMLElement) { convertVMLChild(chapter, svg, (VMLElement) child, scaleX, scaleY); } } } private void convertVMLChild(OPSDocument chapter, SVGElement parentSVG, VMLElement vml, float scaleX, float scaleY) { try { SVGElement childSVG = null; Hashtable style = vml.getStyle(); if (style == null) return; String rotationStr = (String) style.get("rotation"); float rotation = 0; if (rotationStr != null) { try { if (rotationStr.endsWith("fd")) { rotation = Float.parseFloat(rotationStr.substring(0, rotationStr.length() - 2)) / 0x10000; } else { rotation = Float.parseFloat(rotationStr); } } catch (Exception e) { e.printStackTrace(log); } } float top = VMLElement.getNumberValue(style, "top", 0); float left = VMLElement.getNumberValue(style, "left", 0); float width = VMLElement.getNumberValue(style, "width", 0); float height = VMLElement.getNumberValue(style, "height", 0); float cx = left + width / 2; float cy = top + height / 2; String flip = (String) style.get("flip"); float[] textbox = null; if (vml instanceof VMLGroupElement) { // not supported } else if (vml instanceof VMLShapeElement) { childSVG = chapter.createSVGElement("path"); VMLPathConverter conv = new VMLPathConverter((VMLShapeElement) vml); conv.setOuterSize(width, height); conv.setScale(scaleX, scaleY); conv.readFormulas(); String path = conv.getSVGPath(); childSVG.setAttribute("d", path); textbox = conv.getTextBox(); } else if (vml instanceof VMLRectElement) { if (width > 0 && height > 0) { childSVG = chapter.createSVGElement("rect"); childSVG.setAttribute("x", Float.toString(-width / 2)); childSVG.setAttribute("y", Float.toString(-height / 2)); childSVG.setAttribute("width", Float.toString(width)); childSVG.setAttribute("height", Float.toString(height)); } } else if (vml instanceof VMLOvalElement) { if (width > 0 && height > 0) { childSVG = chapter.createSVGElement("ellipse"); childSVG.setAttribute("rx", Float.toString(width / 2)); childSVG.setAttribute("ry", Float.toString(height / 2)); } } else if (vml instanceof VMLLineElement) { VMLCoordPair from = ((VMLLineElement) vml).getFrom(); VMLCoordPair to = ((VMLLineElement) vml).getTo(); if (from != null && to != null) { childSVG = chapter.createSVGElement("line"); childSVG.setAttribute("x1", Float.toString(from.x)); childSVG.setAttribute("y1", Float.toString(from.y)); childSVG.setAttribute("x2", Float.toString(to.x)); childSVG.setAttribute("y2", Float.toString(to.y)); } } if (childSVG != null) { StringBuffer transform = new StringBuffer(); boolean flipX = flip != null && flip.indexOf('x') >= 0; boolean flipY = flip != null && flip.indexOf('y') >= 0; if (cx != 0 || cy != 0) transform.append("translate(" + cx + "," + cy + ")"); if (rotation != 0) transform.append("rotate(" + rotation + ")"); if (flipX || flipY) transform.append("scale(" + (flipX ? -1 : 1) + " " + (flipY ? -1 : 1) + ")"); VMLShadow shadow = vml.getShadow(); if (transform.length() > 0) childSVG.setAttribute("transform", transform.toString()); RGBColor fill = vml.getFill(); childSVG.setAttribute("fill", (fill != null ? fill.toCSSValue().toCSSString() : "none")); RGBColor stroke = vml.getStroke(); if (stroke != null) { childSVG.setAttribute("stroke", stroke.toCSSValue().toCSSString()); String sws = vml.getStrokeWeight(); if (sws != null) { float sw = VMLPathConverter.readCSSLength(sws, 0); if (sw > 0) childSVG.setAttribute("stroke-width", Float.toString(scaleX * sw)); } } if (shadow != null) { SVGElement svgShadow = (SVGElement)childSVG.cloneElementShallow(); String shadowOffset = "translate(" + scaleX * shadow.getOffsetX() + "," + scaleY * shadow.getOffsetY() + ")"; svgShadow.setAttribute("transform", shadowOffset + transform); svgShadow.setAttribute("fill", shadow.getColor().toCSSValue()); if( stroke != null ) svgShadow.setAttribute("stroke", shadow.getColor().toCSSValue()); if (shadow.getOpacity() != 1) svgShadow.setAttribute("opacity", Float.toString(shadow.getOpacity())); parentSVG.add(svgShadow); } float opacity = vml.getOpacity(); if (opacity != 1) childSVG.setAttribute("opacity", Float.toString(opacity)); parentSVG.add(childSVG); TXBXContentElement textboxContent = vml.getTextBoxContentElement(); if (textboxContent != null) { if (textbox == null) textbox = vml.getTextBox(); if (textbox != null) { SVGElement foreignObject = chapter.createSVGElement("foreignObject"); float scaleAdj = Math.round(100 * scaleY) / 100.0f; foreignObject.setAttribute("transform", transform + "scale(" + scaleAdj + " " + scaleAdj + ")"); foreignObject.setAttribute("x", Float.toString(textbox[0] / scaleY)); foreignObject.setAttribute("y", Float.toString(textbox[1] / scaleY)); foreignObject.setAttribute("width", Float.toString((textbox[2] - textbox[0]) / scaleY)); foreignObject.setAttribute("height", Float.toString((textbox[3] - textbox[1]) / scaleY)); parentSVG.add(foreignObject); WordMLConverter wordConv; if (embedded) wordConv = new WordMLConverter(wordConverter, resource); else wordConv = new WordMLConverter(wordConverter, styleConverter); int depth = wordConv.pushOPSContainer(foreignObject); wordConv.appendConvertedElement(textboxContent, null, null, 1, 1, null); wordConv.restoreOPSContainer(depth); } } } } catch (Exception e) { // VML is very incomplete, don't fail the whole document e.printStackTrace(log); } } }