/*******************************************************************************
* 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 com.adobe.dp.office.metafile.GDIBitmap;
import com.adobe.dp.office.metafile.GDIBrush;
import com.adobe.dp.office.metafile.GDIFont;
import com.adobe.dp.office.metafile.GDIMatrix;
import com.adobe.dp.office.metafile.GDIObject;
import com.adobe.dp.office.metafile.GDIPen;
import com.adobe.dp.office.metafile.GDISurface;
public class GDISVGSurface extends GDISurface {
StringBuffer body = new StringBuffer();
StringBuffer defs = new StringBuffer();
boolean initialized = false;
GDIMatrix initialTransform;
int mappingMode;
int[] path = new int[10];
int pathEnd = 0;
ResourceWriter resourceWriter;
public GDISVGSurface(ResourceWriter resourceWriter) {
this.resourceWriter = resourceWriter;
}
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
}
private String makeRGB(int rgb) {
return "#" + Integer.toHexString((rgb & 0xFFFFFF) | 0xF000000).substring(1);
}
private void appendFillAndStrokeParam() {
GDIPen pen = getCurrentPen();
if (pen != null && pen.getStyle() != GDIPen.PS_NULL) {
body.append(" stroke=\"");
body.append(makeRGB(pen.getColor()));
body.append('"');
int width = pen.getWidth();
if (width > 1) {
body.append(" stroke-width=\"");
body.append(width);
body.append('"');
}
}
GDIBrush brush = getCurrentBrush();
if (brush == null || brush.getStyle() == GDIBrush.BS_NULL) {
body.append(" fill=\"none\"");
} else {
body.append(" fill=\"");
body.append(makeRGB(brush.getColor()));
body.append('"');
if( getPolyFillMode() == ALTERNATE ) {
body.append(" fill-rule=\"evenodd\"");
}
}
}
private void captureHeader() {
if (initialized)
return;
initialTransform = getViewportMatrix();
initialized = true;
}
private void ensurePoints(int count) {
if (path.length < pathEnd + 3 * count) {
int[] newPath = new int[2 * pathEnd + 3 * count];
System.arraycopy(path, 0, newPath, 0, pathEnd);
path = newPath;
}
}
private void endPathIfAny() {
if (pathEnd == 0)
return;
body.append("<path d=\"");
int i = 0;
while(i < pathEnd) {
int command = path[i++];
body.append((char)command);
switch(command) {
case 'M' :
case 'L' : {
int x = path[i++];
int y = path[i++];
body.append(x);
if (y >= 0)
body.append(' ');
body.append(y);
break;
}
}
}
body.append('"');
appendFillAndStrokeParam();
body.append("/>\r\n");
pathEnd = 0;
}
public void selectObject(GDIObject obj) {
captureHeader();
endPathIfAny();
super.selectObject(obj);
}
public void extTextOut(int x, int y, String text, int flags, int[] clipRect, int[] adj) {
captureHeader();
endPathIfAny();
GDIFont font = getCurrentFont();
if (font != null) {
int fontSize = font.getHeight();
if (fontSize < 0)
fontSize = -fontSize;
int textAlign = getTextAlign();
int color = getTextColor();
body.append("<text x=\"");
body.append(x);
body.append("\" y=\"");
body.append(y);
switch( textAlign & 6 ) {
case 6 : // center
body.append("\" text-anchor=\"middle");
break;
case 4 : // right
body.append("\" text-anchor=\"end");
break;
}
body.append("\" fill=\"");
body.append(makeRGB(color));
body.append("\" font-family=\"'");
body.append(font.getName());
body.append("'\" font-size=\"");
body.append(fontSize);
body.append('"');
int weight = font.getWeight();
if (weight % 100 == 0) {
body.append(" font-weight=\"");
body.append(weight);
body.append('"');
}
if (font.isItalic()) {
body.append(" font-style=\"italic\"");
}
if (font.isStrikeout() || font.isUnderline()) {
body.append(" text-decoration=\"");
if (font.isUnderline()) {
body.append("underline");
if (font.isStrikeout())
body.append(',');
}
if (font.isStrikeout())
body.append("line-through");
body.append('"');
}
body.append('>');
body.append(text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"));
body.append("</text>\r\n");
}
}
public void lineTo(int x, int y) {
captureHeader();
ensurePoints(1);
path[pathEnd++] = 'L';
path[pathEnd++] = x;
path[pathEnd++] = y;
}
public void moveTo(int x, int y) {
captureHeader();
ensurePoints(1);
path[pathEnd++] = 'M';
path[pathEnd++] = x;
path[pathEnd++] = y;
}
public void closePath() {
captureHeader();
ensurePoints(1);
path[pathEnd++] = 'Z';
}
private void playPoly(int[] points, int offset, int len) {
ensurePoints(len / 2);
for (int i = 0; i < len; i += 2) {
path[pathEnd++] = (i == 0 ? 'M' : 'L');
path[pathEnd++] = points[offset + i];
path[pathEnd++] = points[offset + i + 1];
}
}
public void polygon(int[] points, int offset, int len) {
captureHeader();
endPathIfAny();
playPoly(points, offset, len);
closePath();
endPathIfAny();
}
public void polyPolygon(int[] lens, int points[]) {
captureHeader();
endPathIfAny();
int offset = 0;
for (int i = 0; i < lens.length; i++) {
int len = lens[i] * 2;
playPoly(points, offset, len);
closePath();
offset += len;
}
endPathIfAny();
}
public void polyline(int[] points, int offset, int len) {
captureHeader();
endPathIfAny();
ensurePoints(len / 2);
for (int i = 0; i < len; i += 2) {
path[pathEnd++] = (i == 0 ? 'M' : 'L');
path[pathEnd++] = points[offset + i];
path[pathEnd++] = points[offset + i + 1];
}
endPathIfAny();
}
public void rectangle(int x1, int y1, int x2, int y2) {
captureHeader();
endPathIfAny();
if (x2 < x1) {
int tmp = x2;
x2 = x1;
x1 = tmp;
}
if (y2 < y1) {
int tmp = y2;
y2 = y1;
y1 = tmp;
}
body.append("<rect x=\"");
body.append(x1);
body.append("\" y=\"");
body.append(y1);
body.append("\" width=\"");
body.append(x2 - x1);
body.append("\" height=\"");
body.append(y2 - y1);
body.append('"');
appendFillAndStrokeParam();
body.append("/>\r\n");
}
public void ellipse(int x1, int y1, int x2, int y2) {
captureHeader();
endPathIfAny();
body.append("<ellipse cx=\"");
body.append((x1 + x2) / 2.0);
body.append("\" cy=\"");
body.append((y1 + y2) / 2.0);
body.append("\" rx=\"");
body.append(Math.abs((x2 - x1) / 2.0));
body.append("\" ry=\"");
body.append(Math.abs((y2 - y1) / 2.0));
body.append('"');
appendFillAndStrokeParam();
body.append("/>\r\n");
}
public void stretchDIB(GDIBitmap bitmap, int destX, int destY, int destWidth, int destHeight, int srcX, int srcY,
int srcWidth, int srcHeight) {
if (resourceWriter != null) {
try {
StreamAndName image = resourceWriter.createResource("image", ".png", true);
PNGWriter writer = new PNGWriter(image.stream, bitmap.getWidth(), bitmap.getHeight(), false);
bitmap.saveAsPNG(writer);
writer.close();
boolean flipX = destWidth < 0;
boolean flipY = destHeight < 0;
int width = (flipX ? -destWidth : destWidth);
int height = (flipY ? -destHeight : destHeight);
body.append("<image transform=\"matrix(");
body.append(flipX ? -1 : 1);
body.append(" 0 0 ");
body.append(flipY ? -1 : 1);
body.append(" ");
body.append(destX);
body.append(" ");
body.append(destY);
body.append(")\" width=\"");
body.append(width);
body.append("\" height=\"");
body.append(height);
body.append("\" xlink:href=\"");
body.append(image.name);
body.append("\"/>\r\n");
} catch (Exception err) {
err.printStackTrace();
}
}
}
public String getSVG() {
captureHeader();
StringBuffer total = new StringBuffer();
total.append("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
int wb = getBoundsRight() - getBoundsLeft();
int hb = getBoundsBottom() - getBoundsTop();
double wv = wb / Math.abs(initialTransform.a);
double hv = hb / Math.abs(initialTransform.d);
double xv = -initialTransform.x/initialTransform.a;
double yv = -initialTransform.y/initialTransform.d;
boolean flipX = initialTransform.a < 0;
boolean flipY = initialTransform.d < 0;
if (wb != 0 && hb != 0) {
total.append(" viewBox=\"");
if (flipX)
total.append('0');
else {
total.append(xv);
xv = 0;
}
total.append(' ');
if (flipY)
total.append('0');
else {
total.append(yv / initialTransform.d);
yv = 0;
}
total.append(' ');
total.append(wv);
total.append(' ');
total.append(hv);
total.append("\" width=\"");
total.append(wb);
total.append("\" height=\"");
total.append(hb);
total.append('"');
}
total.append(">\r\n");
if (defs.length() > 0) {
total.append("<defs>\r\n");
total.append(defs);
total.append("</defs>\r\n");
}
if (flipX || flipY || xv != 0 || yv != 0) {
total.append("<g transform=\"matrix(");
total.append(flipX ? "-1" : "1");
total.append(" 0 0 ");
total.append(flipY ? "-1" : "1");
total.append(' ');
total.append(xv);
total.append(' ');
total.append(yv);
total.append(")\">\r\n");
total.append(body);
total.append("</g>\r\n");
} else {
total.append(body);
}
total.append("</svg>\r\n");
return total.toString();
}
}