/*******************************************************************************
* Copyright 2010 Simon Mieth
*
* 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.
******************************************************************************/
/*
* Created on Jun 28, 2004
*
*/
package org.kabeja.svg;
import java.io.File;
import java.net.MalformedURLException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Iterator;
import org.kabeja.common.LineType;
import org.kabeja.common.LineWidth;
import org.kabeja.entities.util.StyledTextParagraph;
import org.kabeja.entities.util.TextDocument;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
*
*
* @author <a href="mailto:simon.mieth@gmx.de">Simon Mieth</a>
*
*/
public class SVGUtils {
public final static String DEFAUL_ATTRIBUTE_TYPE = "CDATA";
public final static String DEFAULT_ID_NAME_PREFIX = "ID";
public final static char DEFAULT_CONVERT_MARKER_CHAR = '_';
private static DecimalFormat format;
static {
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator('.');
format = new DecimalFormat("###.###################################", symbols);
}
public static void startElement(ContentHandler handler, String element, Attributes attr) throws SAXException {
// handler
// .startElement(SVGConstants.SVG_NAMESPACE,
// element,SVGConstants.SVG_PREFIX+":"+element,
// attr);
handler.startElement(SVGConstants.SVG_NAMESPACE, element, element, attr);
}
public static void endElement(ContentHandler handler, String element) throws SAXException {
// handler.endElement(SVGConstants.SVG_NAMESPACE,
// element,SVGConstants.SVG_PREFIX+":"+element);
handler.endElement(SVGConstants.SVG_NAMESPACE, element, element);
}
public static void addAttribute(AttributesImpl attr, String name, String value) {
// we have remove to override
int index = attr.getIndex(name);
if (index > -1) {
attr.removeAttribute(index);
}
attr.addAttribute("", name, name, DEFAUL_ATTRIBUTE_TYPE, value);
}
public static void characters(ContentHandler handler, String text) throws SAXException {
char[] data = text.toCharArray();
handler.characters(data, 0, data.length);
}
public static void emptyElement(ContentHandler handler, String element, Attributes attr) throws SAXException {
startElement(handler, element, attr);
endElement(handler, element);
}
public static void addStrokeDashArrayAttribute(AttributesImpl attr, LineType ltype) {
addStrokeDashArrayAttribute(attr, ltype, 1.0);
}
public static void addStrokeDashArrayAttribute(AttributesImpl attr, LineType ltype, double scale) {
if (ltype != null) {
double[] pattern = ltype.getPattern();
if (pattern.length > 0) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < pattern.length; i++) {
if (pattern[i] != 0.0) {
buf.append(format.format(Math.abs((pattern[i] * scale))));
} else {
// that means a dot
buf.append("0.05%");
}
buf.append(", ");
}
buf.deleteCharAt(buf.length() - 2);
SVGUtils.addAttribute(attr, SVGConstants.SVG_ATTRIBUTE_STROKE_DASHARRAY, buf.toString());
}
}
}
/**
* SVG is XML and needs valid id attributes look at <a
* href="http://www.w3.org/TR/2000/REC-xml-20001006#sec-common-syn"
* >XML-Recommandation </a>.
*
* @param id
* @return a id which should be valid, but may be not unique.
*/
public static String toValidateID(long id) {
return DEFAULT_ID_NAME_PREFIX + id;
}
/**
* SVG is XML and needs valid id attributes look at <a
* href="http://www.w3.org/TR/2000/REC-xml-20001006#sec-common-syn"
* >XML-Recommandation </a>.
*
* @param id
* @return a id which should be valid, but may be not unique.
*/
public static String validateID(String id) {
if (id.length() > 0) {
StringBuffer buf = new StringBuffer();
char first = id.charAt(0);
if (!Character.isLetter(first) && (first != ':')) {
buf.append(DEFAULT_ID_NAME_PREFIX);
}
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);
// TODO we have to allow here CombinigChar and Extender too
if (Character.isLetter(c) || Character.isDigit(c) || (c == '-') || (c == '.') || (c == ':')) {
buf.append(c);
} else {
// normally we have to check all id to guarantee it will be
// a
// unique,
// but we convert the current char to a integer with
// "_"-prefix and "_" suffix
buf.append(DEFAULT_CONVERT_MARKER_CHAR);
buf.append((int) c);
buf.append(DEFAULT_CONVERT_MARKER_CHAR);
}
}
return buf.toString();
} else {
return id;
}
}
/**
* This will reverse a validated ID back to DXF id.
*
* @param id
* @return
*/
public static long reverseID(String id) {
if (id.length() > 0) {
if (id.startsWith(DEFAULT_ID_NAME_PREFIX)) {
try {
return Long.parseLong(id.substring(DEFAULT_ID_NAME_PREFIX.length() - 1));
} catch (Exception e) {
// no dxf generated id
e.printStackTrace();
}
}
}
return -1;
}
public static void textDocumentToSAX(ContentHandler handler, TextDocument doc) throws SAXException {
for (StyledTextParagraph para : doc.getStyledParagraphs()) {
styledTextToSAX(handler, para);
}
}
/**
*
* @param handler
* @param para
* the StyledTextParagraph
* @throws SAXException
*/
public static void styledTextToSAX(ContentHandler handler, StyledTextParagraph para) throws SAXException {
AttributesImpl atts = new AttributesImpl();
String decoration = "";
if (para.isUnderline()) {
decoration += "underline ";
}
if (para.isOverline()) {
decoration += "overline ";
}
if (decoration.length() > 0) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_TEXT_DECORATION, decoration);
}
if (para.getLineIndex() > 0) {
SVGUtils.addAttribute(atts, "dy", "1.3em");
}
if (para.isNewline()) {
SVGUtils.addAttribute(atts, "x", "" + para.getInsertPoint().getX());
para.setNewline(false);
}
if (para.getValign() == StyledTextParagraph.VERTICAL_ALIGNMENT_TOP) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_TEXT_BASELINE_SHIFT, "-100%");
} else if (para.getValign() == StyledTextParagraph.VERTICAL_ALIGNMENT_BOTTOM) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_TEXT_BASELINE_SHIFT, "sub");
} else if (para.getValign() == StyledTextParagraph.VERTICAL_ALIGNMENT_CENTER) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_TEXT_BASELINE_SHIFT, "-40%");
}
if (para.getWidth() > 0.0) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_TEXT_LENGTH, "" + para.getWidth());
}
if (para.isBold()) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_FONT_WEIGHT, "bold");
}
if (para.isItalic()) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_FONT_STYLE, "italic");
}
if (para.getFont().length() > 0) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_FONT_FAMILY, para.getFont());
}
if (para.getFontHeight() > 0) {
SVGUtils.addAttribute(atts, SVGConstants.SVG_ATTRIBUTE_FONT_SIZE, "" + formatNumberAttribute(para.getFontHeight()));
}
atts.addAttribute(SVGConstants.XML_NAMESPACE, "space", "xml:space", "CDATA", "preserve");
SVGUtils.startElement(handler, SVGConstants.SVG_TSPAN, atts);
SVGUtils.characters(handler, para.getText());
SVGUtils.endElement(handler, SVGConstants.SVG_TSPAN);
}
public static String formatNumberAttribute(double v) {
return format.format((float) v);
}
public static String fileToURI(File file) {
StringBuffer buf = new StringBuffer();
try {
buf.append("file://");
char[] c = file.toURL().toExternalForm().toCharArray();
for (int i = 5; i < c.length; i++) {
if (Character.isWhitespace(c[i])) {
buf.append("%20");
} else {
buf.append(c[i]);
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return buf.toString();
}
public static String pathToURI(String path) {
StringBuffer buf = new StringBuffer();
char[] c = path.toCharArray();
if (c.length > 0) {
buf.append("file://");
if (c[0] != '/') {
buf.append('/');
}
for (int i = 0; i < c.length; i++) {
if (Character.isWhitespace(c[i])) {
buf.append("%20");
} else if (c[i] == '\\') {
buf.append('/');
} else {
buf.append(c[i]);
}
}
}
return buf.toString();
}
public static String lineWeightToStrokeWidth(int lineWeight) {
double w = (double) lineWeight / 100.0;
return "" + w + " mm";
}
public static String lineWidthToStrokeWidth(LineWidth lw) {
switch (lw.getType()) {
case LineWidth.TYPE_LINE_WEIGHT:
double w = lw.getValue() / 100.0;
return formatNumberAttribute(w) + " mm";
case LineWidth.TYPE_LINE_WIDTH:
return formatNumberAttribute(lw.getValue());
case LineWidth.TYPE_PERCENT:
return formatNumberAttribute(lw.getValue()) + "%";
default:
return "0.02%";
}
}
}