/*
* Copyright 2007-2012 Hidekatsu Izuno, Shunsuke Mori
*
* 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.
*/
package net.arnx.wmf2svg.gdi.svg;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Logger;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import net.arnx.wmf2svg.gdi.*;
import net.arnx.wmf2svg.util.Base64;
import net.arnx.wmf2svg.util.ImageUtil;
/**
* @author Hidekatsu Izuno
* @author Shunsuke Mori
*/
public class SvgGdi implements Gdi {
private static Logger log = Logger.getLogger(SvgGdi.class.getName());
private boolean compatible;
private Properties props = new Properties();
private SvgDc dc;
private LinkedList saveDC = new LinkedList();
private Document doc = null;
private Element parentNode = null;
private Element styleNode = null;
private Element defsNode = null;
private int brushNo = 0;
private int fontNo = 0;
private int penNo = 0;
private int patternNo = 0;
private int rgnNo = 0;
private int clipPathNo = 0;
private int maskNo = 0;
private Map nameMap = new HashMap();
private StringBuffer buffer = new StringBuffer();
private SvgBrush defaultBrush;
private SvgPen defaultPen;
private SvgFont defaultFont;
public SvgGdi() throws SvgGdiException {
this(false);
}
public SvgGdi(boolean compatible) throws SvgGdiException {
this.compatible = compatible;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new SvgGdiException(e);
}
DOMImplementation dom = builder.getDOMImplementation();
doc = dom.createDocument("http://www.w3.org/2000/svg", "svg", null);
InputStream in = null;
try {
in = getClass().getResourceAsStream("SvgGdi.properties");
props.load(in);
} catch (Exception e) {
throw new SvgGdiException("properties format error: SvgGDI.properties");
} finally {
try {
if (in != null) in.close();
} catch (IOException e) {
// no handle
}
}
}
public SvgDc getDC() {
return dc;
}
public String getProperty(String key) {
return props.getProperty(key);
}
public Document getDocument() {
return doc;
}
public Element getDefsElement() {
return defsNode;
}
public Element getStyleElement() {
return styleNode;
}
public void placeableHeader(int wsx, int wsy, int wex, int wey, int dpi) {
if (parentNode == null) {
init();
}
dc.setWindowExtEx(Math.abs(wex - wsx), Math.abs(wey - wsy), null);
dc.setDpi(dpi);
Element root = doc.getDocumentElement();
root.setAttribute("width", ""
+ (Math.abs(wex - wsx) / (double) dc.getDpi()) + "in");
root.setAttribute("height", ""
+ (Math.abs(wey - wsy) / (double) dc.getDpi()) + "in");
}
public void header() {
if (parentNode == null) {
init();
}
}
private void init() {
dc = new SvgDc(this);
Element root = doc.getDocumentElement();
root.setAttribute("xmlns", "http://www.w3.org/2000/svg");
root.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
defsNode = doc.createElement("defs");
root.appendChild(defsNode);
styleNode = doc.createElement("style");
styleNode.setAttribute("type", "text/css");
root.appendChild(styleNode);
parentNode = doc.createElement("g");
doc.getDocumentElement().appendChild(parentNode);
defaultBrush = (SvgBrush) createBrushIndirect(GdiBrush.BS_SOLID,
0x00FFFFFF, 0);
defaultPen = (SvgPen) createPenIndirect(GdiPen.PS_SOLID, 1,
0x00000000);
defaultFont = null;
dc.setBrush(defaultBrush);
dc.setPen(defaultPen);
dc.setFont(defaultFont);
}
public void animatePalette(GdiPalette palette, int startIndex, int[] entries) {
// TODO
log.fine("not implemented: animatePalette");
}
public void arc(int sxr, int syr, int exr, int eyr, int sxa, int sya,
int exa, int eya) {
double rx = (Math.abs(exr - sxr) - 1) / 2.0;
double ry = (Math.abs(eyr - syr) - 1) / 2.0;
if (rx <= 0 || ry <= 0) return;
double cx = Math.min(sxr, exr) + rx;
double cy = Math.min(syr, eyr) + ry;
Element elem = null;
if (sxa == exa && sya == eya) {
if (rx == ry) {
elem = doc.createElement("circle");
elem.setAttribute("cx", "" + dc.toAbsoluteX(cx));
elem.setAttribute("cy", "" + dc.toAbsoluteY(cy));
elem.setAttribute("r", "" + dc.toRelativeX(rx));
} else {
elem = doc.createElement("ellipse");
elem.setAttribute("cx", "" + dc.toAbsoluteX(cx));
elem.setAttribute("cy", "" + dc.toAbsoluteY(cy));
elem.setAttribute("rx", "" + dc.toRelativeX(rx));
elem.setAttribute("ry", "" + dc.toRelativeY(ry));
}
} else {
double sa = Math.atan2((sya - cy) * rx, (sxa - cx) * ry);
double sx = rx * Math.cos(sa);
double sy = ry * Math.sin(sa);
double ea = Math.atan2((eya - cy) * rx, (exa - cx) * ry);
double ex = rx * Math.cos(ea);
double ey = ry * Math.sin(ea);
double a = Math.atan2((ex-sx) * (-sy) - (ey-sy) * (-sx), (ex-sx) * (-sx) + (ey-sy) * (-sy));
elem = doc.createElement("path");
elem.setAttribute("d", "M " + dc.toAbsoluteX(sx + cx) + "," + dc.toAbsoluteY(sy + cy)
+ " A " + dc.toRelativeX(rx) + "," + dc.toRelativeY(ry)
+ " 0 " + (a > 0 ? "1" : "0") + " 0"
+ " " + dc.toAbsoluteX(ex + cx) + "," + dc.toAbsoluteY(ey + cy));
}
if (dc.getPen() != null) {
elem.setAttribute("class", getClassString(dc.getPen()));
}
elem.setAttribute("fill", "none");
parentNode.appendChild(elem);
}
public void bitBlt(byte[] image, int dx, int dy, int dw, int dh,
int sx, int sy, long rop) {
bmpToSvg(image, dx, dy, dw, dh, sx, sy, dw, dh, Gdi.DIB_RGB_COLORS, rop);
}
public void chord(int sxr, int syr, int exr, int eyr, int sxa, int sya,
int exa, int eya) {
double rx = (Math.abs(exr - sxr) - 1) / 2.0;
double ry = (Math.abs(eyr - syr) - 1) / 2.0;
if (rx <= 0 || ry <= 0) return;
double cx = Math.min(sxr, exr) + rx;
double cy = Math.min(syr, eyr) + ry;
Element elem = null;
if (sxa == exa && sya == eya) {
if (rx == ry) {
elem = doc.createElement("circle");
elem.setAttribute("cx", "" + dc.toAbsoluteX(cx));
elem.setAttribute("cy", "" + dc.toAbsoluteY(cy));
elem.setAttribute("r", "" + dc.toRelativeX(rx));
} else {
elem = doc.createElement("ellipse");
elem.setAttribute("cx", "" + dc.toAbsoluteX(cx));
elem.setAttribute("cy", "" + dc.toAbsoluteY(cy));
elem.setAttribute("rx", "" + dc.toRelativeX(rx));
elem.setAttribute("ry", "" + dc.toRelativeY(ry));
}
} else {
double sa = Math.atan2((sya - cy) * rx, (sxa - cx) * ry);
double sx = rx * Math.cos(sa);
double sy = ry * Math.sin(sa);
double ea = Math.atan2((eya - cy) * rx, (exa - cx) * ry);
double ex = rx * Math.cos(ea);
double ey = ry * Math.sin(ea);
double a = Math.atan2((ex-sx) * (-sy) - (ey-sy) * (-sx), (ex-sx) * (-sx) + (ey-sy) * (-sy));
elem = doc.createElement("path");
elem.setAttribute("d", "M " + dc.toAbsoluteX(sx + cx) + "," + dc.toAbsoluteY(sy + cy)
+ " A " + dc.toRelativeX(rx) + "," + dc.toRelativeY(ry)
+ " 0 " + (a > 0 ? "1" : "0") + " 0"
+ " " + dc.toAbsoluteX(ex + cx) + "," + dc.toAbsoluteY(ey + cy) + " z");
}
if (dc.getPen() != null || dc.getBrush() != null) {
elem.setAttribute("class", getClassString(dc.getPen(), dc.getBrush()));
if (dc.getBrush() != null
&& dc.getBrush().getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(dc.getBrush().createFillPattern(id));
}
}
parentNode.appendChild(elem);
}
public GdiBrush createBrushIndirect(int style, int color, int hatch) {
SvgBrush brush = new SvgBrush(this, style, color, hatch);
if (!nameMap.containsKey(brush)) {
String name = "brush" + (brushNo++);
nameMap.put(brush, name);
styleNode.appendChild(brush.createTextNode(name));
}
return brush;
}
public GdiFont createFontIndirect(int height, int width, int escapement,
int orientation, int weight, boolean italic, boolean underline,
boolean strikeout, int charset, int outPrecision,
int clipPrecision, int quality, int pitchAndFamily, byte[] faceName) {
SvgFont font = new SvgFont(this, height, width, escapement,
orientation, weight, italic, underline, strikeout, charset,
outPrecision, clipPrecision, quality, pitchAndFamily, faceName);
if (!nameMap.containsKey(font)) {
String name = "font" + (fontNo++);
nameMap.put(font, name);
styleNode.appendChild(font.createTextNode(name));
}
return font;
}
public GdiPalette createPalette(int version, int[] entries) {
return new SvgPalette(this, version, entries);
}
public GdiPatternBrush createPatternBrush(byte[] image) {
return new SvgPatternBrush(this, image);
}
public GdiPen createPenIndirect(int style, int width, int color) {
SvgPen pen = new SvgPen(this, style, width, color);
if (!nameMap.containsKey(pen)) {
String name = "pen" + (penNo++);
nameMap.put(pen, name);
styleNode.appendChild(pen.createTextNode(name));
}
return pen;
}
public GdiRegion createRectRgn(int left, int top, int right, int bottom) {
SvgRectRegion rgn = new SvgRectRegion(this, left, top, right, bottom);
if (!nameMap.containsKey(rgn)) {
nameMap.put(rgn, "rgn" + (rgnNo++));
defsNode.appendChild(rgn.createElement());
}
return rgn;
}
public void deleteObject(GdiObject obj) {
if (dc.getBrush() == obj) {
dc.setBrush(defaultBrush);
} else if (dc.getFont() == obj) {
dc.setFont(defaultFont);
} else if (dc.getPen() == obj) {
dc.setPen(defaultPen);
}
}
public void dibBitBlt(byte[] image, int dx, int dy, int dw, int dh,
int sx, int sy, long rop) {
bitBlt(image, dx, dy, dw, dh, sx, sy, rop);
}
public GdiPatternBrush dibCreatePatternBrush(byte[] image, int usage) {
// TODO usage
return new SvgPatternBrush(this, image);
}
public void dibStretchBlt(byte[] image, int dx, int dy, int dw, int dh,
int sx, int sy, int sw, int sh, long rop) {
this.stretchDIBits(dx, dy, dw, dh, sx, sy, sw, sh, image, Gdi.DIB_RGB_COLORS, rop);
}
public void ellipse(int sx, int sy, int ex, int ey) {
Element elem = doc.createElement("ellipse");
if (dc.getPen() != null || dc.getBrush() != null) {
elem.setAttribute("class", getClassString(dc.getPen(), dc
.getBrush()));
if (dc.getBrush() != null
&& dc.getBrush().getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(dc.getBrush().createFillPattern(id));
}
}
elem.setAttribute("cx", "" + (int)dc.toAbsoluteX((sx + ex) / 2));
elem.setAttribute("cy", "" + (int)dc.toAbsoluteY((sy + ey) / 2));
elem.setAttribute("rx", "" + (int)dc.toRelativeX((ex - sx) / 2));
elem.setAttribute("ry", "" + (int)dc.toRelativeY((ey - sy) / 2));
parentNode.appendChild(elem);
}
public void escape(byte[] data) {
}
public int excludeClipRect(int left, int top, int right, int bottom) {
Element mask = dc.getMask();
if (mask != null) {
mask = (Element)mask.cloneNode(true);
String name = "mask" + (maskNo++);
mask.setAttribute("id", name);
defsNode.appendChild(mask);
Element unclip = doc.createElement("rect");
unclip.setAttribute("x", "" + (int)dc.toAbsoluteX(left));
unclip.setAttribute("y", "" + (int)dc.toAbsoluteY(top));
unclip.setAttribute("width", "" + (int)dc.toRelativeX(right - left));
unclip.setAttribute("height", "" + (int)dc.toRelativeY(bottom - top));
unclip.setAttribute("fill", "black");
mask.appendChild(unclip);
dc.setMask(mask);
// TODO
return GdiRegion.COMPLEXREGION;
} else {
return GdiRegion.NULLREGION;
}
}
public void extFloodFill(int x, int y, int color, int type) {
// TODO
log.fine("not implemented: extFloodFill");
}
public void extTextOut(int x, int y, int options, int[] rect, byte[] text, int[] dx) {
Element elem = doc.createElement("text");
int escapement = 0;
boolean vertical = false;
if (dc.getFont() != null) {
elem.setAttribute("class", getClassString(dc.getFont()));
if (dc.getFont().getFaceName().startsWith("@")) {
vertical = true;
escapement = dc.getFont().getEscapement()-2700;
} else {
escapement = dc.getFont().getEscapement();
}
}
elem.setAttribute("fill", SvgObject.toColor(dc.getTextColor()));
// style
buffer.setLength(0);
int align = dc.getTextAlign();
if ((align & (TA_LEFT|TA_CENTER|TA_RIGHT)) == TA_RIGHT) {
buffer.append("text-anchor: end; ");
} else if ((align & (TA_LEFT|TA_CENTER|TA_RIGHT)) == TA_CENTER) {
buffer.append("text-anchor: middle; ");
}
if (compatible) {
buffer.append("dominant-baseline: baseline; ");
} else {
if (vertical) {
elem.setAttribute("writing-mode", "tb");
} else {
if ((align & (TA_BOTTOM|TA_TOP|TA_BASELINE)) == TA_BASELINE) {
buffer.append("dominant-baseline: baseline; ");
} else {
buffer.append("dominant-baseline: text-before-edge; ");
}
}
}
if ((align & TA_RTLREADING) == TA_RTLREADING || (options & ETO_RTLREADING) > 0) {
buffer.append("unicode-bidi: bidi-override; direction: rtl; ");
}
if (dc.getTextSpace() > 0) {
buffer.append("word-spacing: ").append(dc.getTextSpace()).append("; ");
}
if (buffer.length() > 0) {
buffer.setLength(buffer.length()-1);
elem.setAttribute("style", buffer.toString());
}
elem.setAttribute("stroke", "none");
if ((align & (TA_NOUPDATECP|TA_UPDATECP)) == TA_UPDATECP) {
x = dc.getCurrentX();
y = dc.getCurrentY();
}
// x
int ax = (int)dc.toAbsoluteX(x);
int width = 0;
if (vertical) {
elem.setAttribute("x", Integer.toString(ax));
if (dc.getFont() != null) width = Math.abs(dc.getFont().getFontSize());
} else {
if (dc.getFont() != null) {
dx = dc.getFont().validateDx(text, dx);
}
if (dx != null && dx.length > 0) {
for (int i = 0; i < dx.length; i++) {
width += dx[i];
}
int tx = x;
if ((align & (TA_LEFT|TA_CENTER|TA_RIGHT)) == TA_RIGHT) {
tx -= (width-dx[dx.length-1]);
} else if ((align & (TA_LEFT|TA_CENTER|TA_RIGHT)) == TA_CENTER) {
tx -= (width-dx[dx.length-1]) / 2;
}
buffer.setLength(0);
for (int i = 0; i < dx.length; i++) {
if (i > 0) buffer.append(" ");
buffer.append((int)dc.toAbsoluteX(tx));
tx += dx[i];
}
if ((align & (TA_NOUPDATECP|TA_UPDATECP)) == TA_UPDATECP) {
dc.moveToEx(tx, y, null);
}
elem.setAttribute("x", buffer.toString());
} else {
if (dc.getFont() != null) width = Math.abs(dc.getFont().getFontSize() * text.length)/2;
elem.setAttribute("x", Integer.toString(ax));
}
}
// y
int ay = (int)dc.toAbsoluteY(y);
int height = 0;
if (vertical) {
if (dc.getFont() != null) {
dx = dc.getFont().validateDx(text, dx);
}
buffer.setLength(0);
if(align == 0) {
buffer.append(ay + (int)dc.toRelativeY(Math.abs(dc.getFont().getHeight())));
} else {
buffer.append(ay);
}
if (dx != null && dx.length > 0) {
for (int i = 0; i < dx.length - 1; i++) {
height += dx[i];
}
int ty = y;
if ((align & (TA_LEFT|TA_CENTER|TA_RIGHT)) == TA_RIGHT) {
ty -= (height-dx[dx.length-1]);
} else if ((align & (TA_LEFT|TA_CENTER|TA_RIGHT)) == TA_CENTER) {
ty -= (height-dx[dx.length-1]) / 2;
}
for (int i = 0; i < dx.length; i++) {
buffer.append(" ");
buffer.append((int)dc.toAbsoluteY(ty));
ty += dx[i];
}
if ((align & (TA_NOUPDATECP|TA_UPDATECP)) == TA_UPDATECP) {
dc.moveToEx(x, ty, null);
}
} else {
if (dc.getFont() != null) height = Math.abs(dc.getFont().getFontSize() * text.length)/2;
}
elem.setAttribute("y", buffer.toString());
} else {
if (dc.getFont() != null) height = Math.abs(dc.getFont().getFontSize());
if (compatible) {
if ((align & (TA_BOTTOM|TA_TOP|TA_BASELINE)) == TA_TOP) {
elem.setAttribute("y", Integer.toString(ay + (int)dc.toRelativeY(height*0.88)));
} else if ((align & (TA_BOTTOM|TA_TOP|TA_BASELINE)) == TA_BOTTOM) {
elem.setAttribute("y", Integer.toString(ay + rect[3] - rect[1] + (int)dc.toRelativeY(height*0.88)));
} else {
elem.setAttribute("y", Integer.toString(ay));
}
} else {
if((align & (TA_BOTTOM|TA_TOP|TA_BASELINE)) == TA_BOTTOM && rect != null) {
elem.setAttribute("y", Integer.toString(ay + rect[3] - rect[1] - (int)dc.toRelativeY(height)));
} else {
elem.setAttribute("y", Integer.toString(ay));
}
}
}
Element bk = null;
if (dc.getBkMode() == OPAQUE || (options & ETO_OPAQUE) > 0) {
if (rect == null && dc.getFont() != null) {
rect = new int[4];
if (vertical) {
rect[0] = x-(int)(width * 0.85);
if ((align & (TA_LEFT|TA_RIGHT|TA_CENTER)) == TA_RIGHT) {
rect[1] = y-height;
} else if ((align & (TA_LEFT|TA_RIGHT|TA_CENTER)) == TA_CENTER) {
rect[1] = y-height/2;
} else {
rect[1] = y;
}
} else {
if ((align & (TA_LEFT|TA_RIGHT|TA_CENTER)) == TA_RIGHT) {
rect[0] = x-width;
} else if ((align & (TA_LEFT|TA_RIGHT|TA_CENTER)) == TA_CENTER) {
rect[0] = x-width/2;
} else {
rect[0] = x;
}
rect[1] = y;
}
rect[2] = rect[0] + width;
rect[3] = rect[1] + height;
}
bk = doc.createElement("rect");
bk.setAttribute("x", Integer.toString((int)dc.toAbsoluteX(rect[0])));
bk.setAttribute("y", Integer.toString((int)dc.toAbsoluteY(rect[1])));
bk.setAttribute("width", Integer.toString((int)dc.toRelativeX(rect[2] - rect[0])));
bk.setAttribute("height", Integer.toString((int)dc.toRelativeY(rect[3] - rect[1])));
bk.setAttribute("fill", SvgObject.toColor(dc.getBkColor()));
}
Element clip = null;
if ((options & ETO_CLIPPED) > 0) {
String name = "clipPath" + (clipPathNo++);
clip = doc.createElement("clipPath");
clip.setAttribute("id", name);
clip.setIdAttribute("id", true);
Element clipRect = doc.createElement("rect");
clipRect.setAttribute("x", Integer.toString((int)dc.toAbsoluteX(rect[0])));
clipRect.setAttribute("y", Integer.toString((int)dc.toAbsoluteY(rect[1])));
clipRect.setAttribute("width", Integer.toString((int)dc.toRelativeX(rect[2] - rect[0])));
clipRect.setAttribute("height", Integer.toString((int)dc.toRelativeY(rect[3] - rect[1])));
clip.appendChild(clipRect);
elem.setAttribute("clip-path", "url(#" + name + ")");
}
String str = null;
if (dc.getFont() != null) {
str = GdiUtils.convertString(text, dc.getFont().getCharset());
} else {
str = GdiUtils.convertString(text, GdiFont.DEFAULT_CHARSET);
}
if (dc.getFont() != null && dc.getFont().getLang() != null) {
elem.setAttribute("xml:lang", dc.getFont().getLang());
}
elem.setAttribute("xml:space", "preserve");
if (compatible) {
str = str.replaceAll("\\r\\n|[\\t\\r\\n ]", "\u00A0");
}
elem.appendChild(doc.createTextNode(str));
if (bk != null || clip != null) {
Element g = doc.createElement("g");
if (bk != null) g.appendChild(bk);
if (clip != null) g.appendChild(clip);
g.appendChild(elem);
elem = g;
}
if (escapement != 0) {
elem.setAttribute("transform", "rotate(" + (-escapement/10.0) + ", " + ax + ", " + ay + ")");
}
parentNode.appendChild(elem);
}
public void fillRgn(GdiRegion rgn, GdiBrush brush) {
if (rgn == null) return;
Element elem = doc.createElement("use");
elem.setAttribute("xlink:href", "url(#" + nameMap.get(rgn) + ")");
elem.setAttribute("class", getClassString(brush));
SvgBrush sbrush = (SvgBrush)brush;
if(sbrush.getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(sbrush.createFillPattern(id));
}
parentNode.appendChild(elem);
}
public void floodFill(int x, int y, int color) {
// TODO
log.fine("not implemented: floodFill");
}
public void frameRgn(GdiRegion rgn, GdiBrush brush, int width, int height) {
// TODO
log.fine("not implemented: frameRgn");
}
public void intersectClipRect(int left, int top, int right, int bottom) {
// TODO
log.fine("not implemented: intersectClipRect");
}
public void invertRgn(GdiRegion rgn) {
if (rgn == null) return;
Element elem = doc.createElement("use");
elem.setAttribute("xlink:href", "url(#" + nameMap.get(rgn) + ")");
String ropFilter = dc.getRopFilter(DSTINVERT);
if (ropFilter != null) {
elem.setAttribute("filter", ropFilter);
}
parentNode.appendChild(elem);
}
public void lineTo(int ex, int ey) {
Element elem = doc.createElement("line");
if (dc.getPen() != null) {
elem.setAttribute("class", getClassString(dc.getPen()));
}
elem.setAttribute("fill", "none");
elem.setAttribute("x1", "" + (int)dc.toAbsoluteX(dc.getCurrentX()));
elem.setAttribute("y1", "" + (int)dc.toAbsoluteY(dc.getCurrentY()));
elem.setAttribute("x2", "" + (int)dc.toAbsoluteX(ex));
elem.setAttribute("y2", "" + (int)dc.toAbsoluteY(ey));
parentNode.appendChild(elem);
dc.moveToEx(ex, ey, null);
}
public void moveToEx(int x, int y, Point old) {
dc.moveToEx(x, y, old);
}
public void offsetClipRgn(int x, int y) {
dc.offsetClipRgn(x, y);
Element mask = dc.getMask();
if (mask != null) {
mask = (Element)mask.cloneNode(true);
String name = "mask" + (maskNo++);
mask.setAttribute("id", name);
if (dc.getOffsetClipX() != 0 || dc.getOffsetClipY() != 0) {
mask.setAttribute("transform", "translate(" + dc.getOffsetClipX() + "," + dc.getOffsetClipY() + ")");
}
defsNode.appendChild(mask);
if (!parentNode.hasChildNodes()) {
doc.getDocumentElement().removeChild(parentNode);
}
parentNode = doc.createElement("g");
parentNode.setAttribute("mask", name);
doc.getDocumentElement().appendChild(parentNode);
dc.setMask(mask);
}
}
public void offsetViewportOrgEx(int x, int y, Point point) {
dc.offsetViewportOrgEx(x, y, point);
}
public void offsetWindowOrgEx(int x, int y, Point point) {
dc.offsetWindowOrgEx(x, y, point);
}
public void paintRgn(GdiRegion rgn) {
fillRgn(rgn, dc.getBrush());
}
public void patBlt(int x, int y, int width, int height, long rop) {
// TODO
log.fine("not implemented: patBlt");
}
public void pie(int sxr, int syr, int exr, int eyr, int sxa, int sya,
int exa, int eya) {
double rx = (Math.abs(exr - sxr) - 1) / 2.0;
double ry = (Math.abs(eyr - syr) - 1) / 2.0;
if (rx <= 0 || ry <= 0) return;
double cx = Math.min(sxr, exr) + rx;
double cy = Math.min(syr, eyr) + ry;
Element elem = null;
if (sxa == exa && sya == eya) {
if (rx == ry) {
elem = doc.createElement("circle");
elem.setAttribute("cx", "" + dc.toAbsoluteX(cx));
elem.setAttribute("cy", "" + dc.toAbsoluteY(cy));
elem.setAttribute("r", "" + dc.toRelativeX(rx));
} else {
elem = doc.createElement("ellipse");
elem.setAttribute("cx", "" + dc.toAbsoluteX(cx));
elem.setAttribute("cy", "" + dc.toAbsoluteY(cy));
elem.setAttribute("rx", "" + dc.toRelativeX(rx));
elem.setAttribute("ry", "" + dc.toRelativeY(ry));
}
} else {
double sa = Math.atan2((sya - cy) * rx, (sxa - cx) * ry);
double sx = rx * Math.cos(sa);
double sy = ry * Math.sin(sa);
double ea = Math.atan2((eya - cy) * rx, (exa - cx) * ry);
double ex = rx * Math.cos(ea);
double ey = ry * Math.sin(ea);
double a = Math.atan2((ex-sx) * (-sy) - (ey-sy) * (-sx), (ex-sx) * (-sx) + (ey-sy) * (-sy));
elem = doc.createElement("path");
elem.setAttribute("d", "M " + dc.toAbsoluteX(sx + cx) + "," + dc.toAbsoluteY(sy + cy)
+ " L " + dc.toAbsoluteX(sx + cx) + "," + dc.toAbsoluteY(sy + cy)
+ " A " + dc.toRelativeX(rx) + "," + dc.toRelativeY(ry)
+ " 0 " + (a > 0 ? "1" : "0") + " 0"
+ " " + dc.toAbsoluteX(ex + cx) + "," + dc.toAbsoluteY(ey + cy) + " z");
}
if (dc.getPen() != null || dc.getBrush() != null) {
elem.setAttribute("class", getClassString(dc.getPen(), dc
.getBrush()));
if (dc.getBrush() != null
&& dc.getBrush().getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(dc.getBrush().createFillPattern(id));
}
}
parentNode.appendChild(elem);
}
public void polygon(Point[] points) {
Element elem = doc.createElement("polygon");
if (dc.getPen() != null || dc.getBrush() != null) {
elem.setAttribute("class", getClassString(dc.getPen(), dc
.getBrush()));
if (dc.getBrush() != null
&& dc.getBrush().getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(dc.getBrush().createFillPattern(id));
}
if (dc.getPolyFillMode() == WINDING) {
elem.setAttribute("fill-rule", "nonzero");
}
}
buffer.setLength(0);
for (int i = 0; i < points.length; i++) {
if (i != 0) {
buffer.append(" ");
}
buffer.append((int)dc.toAbsoluteX(points[i].x)).append(",");
buffer.append((int)dc.toAbsoluteY(points[i].y));
}
elem.setAttribute("points", buffer.toString());
parentNode.appendChild(elem);
}
public void polyline(Point[] points) {
Element elem = doc.createElement("polyline");
if (dc.getPen() != null) {
elem.setAttribute("class", getClassString(dc.getPen()));
}
elem.setAttribute("fill", "none");
buffer.setLength(0);
for (int i = 0; i < points.length; i++) {
if (i != 0)
buffer.append(" ");
buffer.append((int)dc.toAbsoluteX(points[i].x)).append(",");
buffer.append((int)dc.toAbsoluteY(points[i].y));
}
elem.setAttribute("points", buffer.toString());
parentNode.appendChild(elem);
}
public void polyPolygon(Point[][] points) {
Element elem = doc.createElement("path");
if (dc.getPen() != null || dc.getBrush() != null) {
elem.setAttribute("class", getClassString(dc.getPen(), dc
.getBrush()));
if (dc.getBrush() != null
&& dc.getBrush().getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(dc.getBrush().createFillPattern(id));
}
if (dc.getPolyFillMode() == WINDING) {
elem.setAttribute("fill-rule", "nonzero");
}
}
buffer.setLength(0);
for (int i = 0; i < points.length; i++) {
if (i != 0) {
buffer.append(" ");
}
for (int j = 0; j < points[i].length; j++) {
if (j == 0) {
buffer.append("M ");
} else if (j == 1) {
buffer.append(" L ");
}
buffer.append((int)dc.toAbsoluteX(points[i][j].x)).append(",");
buffer.append((int)dc.toAbsoluteY(points[i][j].y)).append(" ");
if (j == points[i].length - 1) {
buffer.append("z");
}
}
}
elem.setAttribute("d", buffer.toString());
parentNode.appendChild(elem);
}
public void realizePalette() {
// TODO
log.fine("not implemented: realizePalette");
}
public void restoreDC(int savedDC) {
int limit = (savedDC < 0) ? -savedDC : saveDC.size()-savedDC;
for (int i = 0; i < limit; i++) {
dc = (SvgDc)saveDC.removeLast();
}
if (!parentNode.hasChildNodes()) {
doc.getDocumentElement().removeChild(parentNode);
}
parentNode = doc.createElement("g");
Element mask = dc.getMask();
if (mask != null) {
parentNode.setAttribute("mask", "url(#" + mask.getAttribute("id") + ")");
}
doc.getDocumentElement().appendChild(parentNode);
}
public void rectangle(int sx, int sy, int ex, int ey) {
Element elem = doc.createElement("rect");
if (dc.getPen() != null || dc.getBrush() != null) {
elem.setAttribute("class", getClassString(dc.getPen(), dc
.getBrush()));
if (dc.getBrush() != null
&& dc.getBrush().getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(dc.getBrush().createFillPattern(id));
}
}
elem.setAttribute("x", "" + (int)dc.toAbsoluteX(sx));
elem.setAttribute("y", "" + (int)dc.toAbsoluteY(sy));
elem.setAttribute("width", "" + (int)dc.toRelativeX(ex - sx));
elem.setAttribute("height", "" + (int)dc.toRelativeY(ey - sy));
parentNode.appendChild(elem);
}
public void resizePalette(GdiPalette palette) {
// TODO
log.fine("not implemented: ResizePalette");
}
public void roundRect(int sx, int sy, int ex, int ey, int rw, int rh) {
Element elem = doc.createElement("rect");
if (dc.getPen() != null || dc.getBrush() != null) {
elem.setAttribute("class", getClassString(dc.getPen(), dc
.getBrush()));
if (dc.getBrush() != null
&& dc.getBrush().getStyle() == GdiBrush.BS_HATCHED) {
String id = "pattern" + (patternNo++);
elem.setAttribute("fill", "url(#" + id + ")");
defsNode.appendChild(dc.getBrush().createFillPattern(id));
}
}
elem.setAttribute("x", "" + (int)dc.toAbsoluteX(sx));
elem.setAttribute("y", "" + (int)dc.toAbsoluteY(sy));
elem.setAttribute("width", "" + (int)dc.toRelativeX(ex - sx));
elem.setAttribute("height", "" + (int)dc.toRelativeY(ey - sy));
elem.setAttribute("rx", "" + (int)dc.toRelativeX(rw));
elem.setAttribute("ry", "" + (int)dc.toRelativeY(rh));
parentNode.appendChild(elem);
}
public void seveDC() {
saveDC.add(dc.clone());
}
public void scaleViewportExtEx(int x, int xd, int y, int yd, Size old) {
dc.scaleViewportExtEx(x, xd, y, yd, old);
}
public void scaleWindowExtEx(int x, int xd, int y, int yd, Size old) {
dc.scaleWindowExtEx(x, xd, y, yd, old);
}
public void selectClipRgn(GdiRegion rgn) {
if (!parentNode.hasChildNodes()) {
doc.getDocumentElement().removeChild(parentNode);
}
parentNode = doc.createElement("g");
if (rgn != null) {
Element mask = doc.createElement("mask");
mask.setAttribute("id", "mask" + (maskNo++));
mask.setIdAttribute("id", true);
if (dc.getOffsetClipX() != 0 || dc.getOffsetClipY() != 0) {
mask.setAttribute("transform", "translate(" + dc.getOffsetClipX() + "," + dc.getOffsetClipY() + ")");
}
defsNode.appendChild(mask);
Element clip = doc.createElement("use");
clip.setAttribute("xlink:href", "url(#" + nameMap.get(rgn) + ")");
clip.setAttribute("fill", "white");
mask.appendChild(clip);
parentNode.setAttribute("mask", "url(#" + mask.getAttribute("id") + ")");
}
doc.getDocumentElement().appendChild(parentNode);
}
public void selectObject(GdiObject obj) {
if (obj instanceof SvgBrush) {
dc.setBrush((SvgBrush) obj);
} else if (obj instanceof SvgFont) {
dc.setFont((SvgFont) obj);
} else if (obj instanceof SvgPen) {
dc.setPen((SvgPen) obj);
}
}
public void selectPalette(GdiPalette palette, boolean mode) {
// TODO
log.fine("not implemented: selectPalette");
}
public void setBkColor(int color) {
dc.setBkColor(color);
}
public void setBkMode(int mode) {
dc.setBkMode(mode);
}
public void setDIBitsToDevice(int dx, int dy, int dw, int dh, int sx,
int sy, int startscan, int scanlines, byte[] image, int colorUse) {
stretchDIBits(dx, dy, dw, dh, sx, sy, dw, dh, image, colorUse, SRCCOPY);
}
public void setLayout(long layout) {
dc.setLayout(layout);
}
public void setMapMode(int mode) {
dc.setMapMode(mode);
}
public void setMapperFlags(long flags) {
dc.setMapperFlags(flags);
}
public void setPaletteEntries(GdiPalette palette, int startIndex, int[] entries) {
// TODO
log.fine("not implemented: setPaletteEntries");
}
public void setPixel(int x, int y, int color) {
Element elem = doc.createElement("rect");
elem.setAttribute("stroke", "none");
elem.setAttribute("fill", SvgPen.toColor(color));
elem.setAttribute("x", "" + (int)dc.toAbsoluteX(x));
elem.setAttribute("y", "" + (int)dc.toAbsoluteY(y));
elem.setAttribute("width", "" + (int)dc.toRelativeX(1));
elem.setAttribute("height", "" + (int)dc.toRelativeY(1));
parentNode.appendChild(elem);
}
public void setPolyFillMode(int mode) {
dc.setPolyFillMode(mode);
}
public void setRelAbs(int mode) {
dc.setRelAbs(mode);
}
public void setROP2(int mode) {
dc.setROP2(mode);
}
public void setStretchBltMode(int mode) {
dc.setStretchBltMode(mode);
}
public void setTextAlign(int align) {
dc.setTextAlign(align);
}
public void setTextCharacterExtra(int extra) {
dc.setTextCharacterExtra(extra);
}
public void setTextColor(int color) {
dc.setTextColor(color);
}
public void setTextJustification(int breakExtra, int breakCount) {
if (breakCount > 0) {
dc.setTextSpace(Math.abs((int)dc.toRelativeX(breakExtra)) / breakCount);
}
}
public void setViewportExtEx(int x, int y, Size old) {
dc.setViewportExtEx(x, y, old);
}
public void setViewportOrgEx(int x, int y, Point old) {
dc.setViewportOrgEx(x, y, old);
}
public void setWindowExtEx(int width, int height, Size old) {
dc.setWindowExtEx(width, height, old);
}
public void setWindowOrgEx(int x, int y, Point old) {
dc.setWindowOrgEx(x, y, old);
}
public void stretchBlt(byte[] image, int dx, int dy, int dw, int dh, int sx, int sy,
int sw, int sh, long rop) {
dibStretchBlt(image, dx, dy, dw, dh, sx, sy, sw, sh, rop);
}
public void stretchDIBits(int dx, int dy, int dw, int dh, int sx, int sy,
int sw, int sh, byte[] image, int usage, long rop) {
bmpToSvg(image, dx, dy, dw, dh, sx, sy, sw, sh, usage, rop);
}
public void textOut(int x, int y, byte[] text) {
Element elem = doc.createElement("text");
int escapement = 0;
boolean vertical = false;
if (dc.getFont() != null) {
elem.setAttribute("class", getClassString(dc.getFont()));
if (dc.getFont().getFaceName().startsWith("@")) {
vertical = true;
escapement = dc.getFont().getEscapement()-2700;
} else {
escapement = dc.getFont().getEscapement();
}
}
elem.setAttribute("fill", SvgObject.toColor(dc.getTextColor()));
// style
buffer.setLength(0);
int align = dc.getTextAlign();
if ((align & (TA_LEFT|TA_RIGHT|TA_CENTER)) == TA_RIGHT) {
buffer.append("text-anchor: end; ");
} else if ((align & (TA_LEFT|TA_RIGHT|TA_CENTER)) == TA_CENTER) {
buffer.append("text-anchor: middle; ");
}
if (vertical) {
elem.setAttribute("writing-mode", "tb");
buffer.append("dominant-baseline: ideographic; ");
} else {
if ((align & (TA_BOTTOM|TA_TOP|TA_BASELINE)) == TA_BASELINE) {
buffer.append("dominant-baseline: baseline; ");
} else {
buffer.append("dominant-baseline: text-before-edge; ");
}
}
if ((align & TA_RTLREADING) == TA_RTLREADING) {
buffer.append("unicode-bidi: bidi-override; direction: rtl; ");
}
if (dc.getTextSpace() > 0) {
buffer.append("word-spacing: " + dc.getTextSpace() + "; ");
}
if (buffer.length() > 0) {
buffer.setLength(buffer.length()-1);
elem.setAttribute("style", buffer.toString());
}
elem.setAttribute("stroke", "none");
int ax = (int)dc.toAbsoluteX(x);
int ay = (int)dc.toAbsoluteY(y);
elem.setAttribute("x", Integer.toString(ax));
elem.setAttribute("y", Integer.toString(ay));
if (escapement != 0) {
elem.setAttribute("transform", "rotate(" + (-escapement/10.0) + ", " + ax + ", " + ay + ")");
}
String str = null;
if (dc.getFont() != null) {
str = GdiUtils.convertString(text, dc.getFont().getCharset());
} else {
str = GdiUtils.convertString(text, GdiFont.DEFAULT_CHARSET);
}
if (dc.getTextCharacterExtra() != 0) {
buffer.setLength(0);
for (int i = 0; i < str.length() - 1; i++) {
if (i != 0) {
buffer.append(" ");
}
buffer.append((int)dc.toRelativeX(dc.getTextCharacterExtra()));
}
elem.setAttribute("dx", buffer.toString());
}
if (dc.getFont() != null && dc.getFont().getLang() != null) {
elem.setAttribute("xml:lang", dc.getFont().getLang());
}
elem.setAttribute("xml:space", "preserve");
if (compatible) {
str = str.replaceAll("\\r\\n|[\\t\\r\\n ]", "\u00A0");
}
elem.appendChild(doc.createTextNode(str));
parentNode.appendChild(elem);
}
public void footer() {
Element root = doc.getDocumentElement();
if (!root.hasAttribute("width") && dc.getWindowWidth() != 0) {
root.setAttribute("width", "" + Math.abs(dc.getWindowWidth()));
}
if (!root.hasAttribute("height") && dc.getWindowHeight() != 0) {
root.setAttribute("height", "" + Math.abs(dc.getWindowHeight()));
}
if (dc.getWindowWidth() != 0 && dc.getWindowHeight() != 0) {
root.setAttribute("viewBox", "0 0 " + Math.abs(dc.getWindowWidth()) + " " + Math.abs(dc.getWindowHeight()));
root.setAttribute("preserveAspectRatio", "none");
}
root.setAttribute("stroke-linecap", "round");
root.setAttribute("fill-rule", "evenodd");
if (!styleNode.hasChildNodes()) {
root.removeChild(styleNode);
} else {
styleNode.insertBefore(doc.createTextNode("\n"), styleNode.getFirstChild());
}
if (!defsNode.hasChildNodes()) {
root.removeChild(defsNode);
}
}
private String getClassString(GdiObject obj1, GdiObject obj2) {
String name1 = getClassString(obj1);
String name2 = getClassString(obj2);
if (name1 != null && name2 != null) {
return name1 + " " + name2;
}
if (name1 != null) {
return name1;
}
if (name2 != null) {
return name2;
}
return "";
}
private String getClassString(GdiObject style) {
if (style == null) {
return "";
}
return (String) nameMap.get(style);
}
private void bmpToSvg(byte[] image, int dx, int dy, int dw, int dh, int sx, int sy,
int sw, int sh, int usage, long rop) {
if (image == null) {
// TODO
return;
}
image = ImageUtil.convert(dibToBmp(image), "png", dh < 0);
StringBuffer buffer = new StringBuffer("data:image/png;base64,");
buffer.append(Base64.encode(image));
String data = buffer.toString();
if (data == null || data.equals("")) {
return;
}
Element elem = doc.createElement("image");
int x = (int)dc.toAbsoluteX(dx);
int y = (int)dc.toAbsoluteY(dy);
int width = (int)dc.toRelativeX(dw);
int height = (int)dc.toRelativeY(dh);
if (width < 0 && height < 0) {
elem.setAttribute("transform", "scale(-1, -1) translate(" + -x + ", " + -y + ")");
} else if (width < 0) {
elem.setAttribute("transform", "scale(-1, 1) translate(" + -x + ", " + y + ")");
} else if (height < 0) {
elem.setAttribute("transform", "scale(1, -1) translate(" + x + ", " + -y + ")");
} else {
elem.setAttribute("x", "" + x);
elem.setAttribute("y", "" + y);
}
elem.setAttribute("width", "" + Math.abs(width));
elem.setAttribute("height", "" + Math.abs(height));
if (sx != 0 || sy != 0 || sw != dw || sh != dh) {
elem.setAttribute("viewBox", "" + sx + " " + sy + " " + sw + " "+ sh);
elem.setAttribute("preserveAspectRatio", "none");
}
String ropFilter = dc.getRopFilter(rop);
if (ropFilter != null) {
elem.setAttribute("filter", ropFilter);
}
elem.setAttribute("xlink:href", data);
parentNode.appendChild(elem);
}
private byte[] dibToBmp(byte[] dib) {
byte[] data = new byte[14 + dib.length];
/* BitmapFileHeader */
data[0] = 0x42; // 'B'
data[1] = 0x4d; // 'M'
long bfSize = data.length;
data[2] = (byte) (bfSize & 0xff);
data[3] = (byte) ((bfSize >> 8) & 0xff);
data[4] = (byte) ((bfSize >> 16) & 0xff);
data[5] = (byte) ((bfSize >> 24) & 0xff);
// reserved 1
data[6] = 0x00;
data[7] = 0x00;
// reserved 2
data[8] = 0x00;
data[9] = 0x00;
// offset
long bfOffBits = 14;
/* BitmapInfoHeader */
long biSize = (dib[0] & 0xff) + ((dib[1] & 0xff) << 8)
+ ((dib[2] & 0xff) << 16) + ((dib[3] & 0xff) << 24);
bfOffBits += biSize;
int biBitCount = (dib[14] & 0xff) + ((dib[15] & 0xff) << 8);
long clrUsed = (dib[32] & 0xff) + ((dib[33] & 0xff) << 8)
+ ((dib[34] & 0xff) << 16) + ((dib[35] & 0xff) << 24);
switch (biBitCount) {
case 1:
bfOffBits += (0x1L + 1) * 4;
break;
case 4:
bfOffBits += (0xFL + 1) * 4;
break;
case 8:
bfOffBits += (0xFFL + 1) * 4;
break;
case 16:
bfOffBits += (clrUsed == 0L) ? 0 : (0xFFFFL + 1) * 4;
break;
case 24:
bfOffBits += (clrUsed == 0L) ? 0 : (0xFFFFFFL + 1) * 4;
break;
case 32:
bfOffBits += (clrUsed == 0L) ? 0 : (0xFFFFFFFFL + 1) * 4;
break;
}
data[10] = (byte) (bfOffBits & 0xff);
data[11] = (byte) ((bfOffBits >> 8) & 0xff);
data[12] = (byte) ((bfOffBits >> 16) & 0xff);
data[13] = (byte) ((bfOffBits >> 24) & 0xff);
System.arraycopy(dib, 0, data, 14, dib.length);
return data;
}
}