465 lines
19 KiB
Java
465 lines
19 KiB
Java
package net.sf.jasperreports.engine.util;
|
|
|
|
import java.awt.Color;
|
|
import java.awt.GraphicsEnvironment;
|
|
import java.awt.font.TextAttribute;
|
|
import java.io.IOException;
|
|
import java.io.StringReader;
|
|
import java.lang.ref.SoftReference;
|
|
import java.text.AttributedCharacterIterator;
|
|
import java.text.AttributedString;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.StringTokenizer;
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
import net.sf.jasperreports.engine.JRRuntimeException;
|
|
import net.sf.jasperreports.engine.xml.JRXmlConstants;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.NamedNodeMap;
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
import org.xml.sax.ErrorHandler;
|
|
import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
import org.xml.sax.SAXParseException;
|
|
|
|
public class JRStyledTextParser implements ErrorHandler {
|
|
private static final String ROOT_START = "<st>";
|
|
|
|
private static final String ROOT_END = "</st>";
|
|
|
|
private static final String NODE_style = "style";
|
|
|
|
private static final String NODE_bold = "b";
|
|
|
|
private static final String NODE_italic = "i";
|
|
|
|
private static final String NODE_underline = "u";
|
|
|
|
private static final String NODE_sup = "sup";
|
|
|
|
private static final String NODE_sub = "sub";
|
|
|
|
private static final String NODE_font = "font";
|
|
|
|
private static final String NODE_br = "br";
|
|
|
|
private static final String NODE_li = "li";
|
|
|
|
private static final String ATTRIBUTE_fontName = "fontName";
|
|
|
|
private static final String ATTRIBUTE_fontFace = "face";
|
|
|
|
private static final String ATTRIBUTE_color = "color";
|
|
|
|
private static final String ATTRIBUTE_size = "size";
|
|
|
|
private static final String ATTRIBUTE_isBold = "isBold";
|
|
|
|
private static final String ATTRIBUTE_isItalic = "isItalic";
|
|
|
|
private static final String ATTRIBUTE_isUnderline = "isUnderline";
|
|
|
|
private static final String ATTRIBUTE_isStrikeThrough = "isStrikeThrough";
|
|
|
|
private static final String ATTRIBUTE_forecolor = "forecolor";
|
|
|
|
private static final String ATTRIBUTE_backcolor = "backcolor";
|
|
|
|
private static final String ATTRIBUTE_pdfFontName = "pdfFontName";
|
|
|
|
private static final String ATTRIBUTE_pdfEncoding = "pdfEncoding";
|
|
|
|
private static final String ATTRIBUTE_isPdfEmbedded = "isPdfEmbedded";
|
|
|
|
private static final String SPACE = " ";
|
|
|
|
private static final String EQUAL_QUOTE = "=\"";
|
|
|
|
private static final String QUOTE = "\"";
|
|
|
|
private static final String SHARP = "#";
|
|
|
|
private static final String LESS = "<";
|
|
|
|
private static final String LESS_SLASH = "</";
|
|
|
|
private static final String GREATER = ">";
|
|
|
|
private static final ThreadLocal threadInstances = new ThreadLocal();
|
|
|
|
public static JRStyledTextParser getInstance() {
|
|
JRStyledTextParser instance = null;
|
|
SoftReference instanceRef = threadInstances.get();
|
|
if (instanceRef != null)
|
|
instance = instanceRef.get();
|
|
if (instance == null) {
|
|
instance = new JRStyledTextParser();
|
|
threadInstances.set(new SoftReference(instance));
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
private DocumentBuilder documentBuilder = null;
|
|
|
|
public JRStyledTextParser() {
|
|
try {
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
this.documentBuilder = factory.newDocumentBuilder();
|
|
this.documentBuilder.setErrorHandler(this);
|
|
} catch (ParserConfigurationException e) {
|
|
throw new JRRuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public JRStyledText parse(Map attributes, String text) throws SAXException {
|
|
JRStyledText styledText = new JRStyledText();
|
|
Document document = null;
|
|
try {
|
|
document = this.documentBuilder.parse(new InputSource(new StringReader("<st>" + text + "</st>")));
|
|
} catch (IOException e) {
|
|
throw new JRRuntimeException(e);
|
|
}
|
|
parseStyle(styledText, document.getDocumentElement());
|
|
styledText.setGlobalAttributes(attributes);
|
|
return styledText;
|
|
}
|
|
|
|
public JRStyledText getStyledText(Map parentAttributes, String text, boolean isStyledText) {
|
|
JRStyledText styledText = null;
|
|
if (isStyledText)
|
|
try {
|
|
styledText = parse(parentAttributes, text);
|
|
} catch (SAXException e) {}
|
|
if (styledText == null) {
|
|
styledText = new JRStyledText();
|
|
styledText.append(text);
|
|
styledText.setGlobalAttributes(parentAttributes);
|
|
}
|
|
return styledText;
|
|
}
|
|
|
|
public String write(JRStyledText styledText) {
|
|
return write(styledText.getGlobalAttributes(), styledText.getAttributedString().getIterator(), styledText.getText());
|
|
}
|
|
|
|
public String write(Map parentAttrs, AttributedCharacterIterator iterator, String text) {
|
|
StringBuffer sbuffer = new StringBuffer();
|
|
int runLimit = 0;
|
|
while (runLimit < iterator.getEndIndex() && (runLimit = iterator.getRunLimit()) <= iterator.getEndIndex()) {
|
|
String chunk = text.substring(iterator.getIndex(), runLimit);
|
|
Map attrs = iterator.getAttributes();
|
|
StringBuffer styleBuffer = writeStyleAttributes(parentAttrs, attrs);
|
|
if (styleBuffer.length() > 0) {
|
|
sbuffer.append("<");
|
|
sbuffer.append("style");
|
|
sbuffer.append(styleBuffer.toString());
|
|
sbuffer.append(">");
|
|
writeChunk(sbuffer, parentAttrs, attrs, chunk);
|
|
sbuffer.append("</");
|
|
sbuffer.append("style");
|
|
sbuffer.append(">");
|
|
} else {
|
|
writeChunk(sbuffer, parentAttrs, attrs, chunk);
|
|
}
|
|
iterator.setIndex(runLimit);
|
|
}
|
|
return sbuffer.toString();
|
|
}
|
|
|
|
public String write(JRStyledText styledText, int startIndex, int endIndex) {
|
|
AttributedCharacterIterator subIterator = (new AttributedString(styledText.getAttributedString().getIterator(), startIndex, endIndex)).getIterator();
|
|
String subText = styledText.getText().substring(startIndex, endIndex);
|
|
return write(styledText.getGlobalAttributes(), subIterator, subText);
|
|
}
|
|
|
|
public void writeChunk(StringBuffer sbuffer, Map parentAttrs, Map attrs, String chunk) {
|
|
Object value = attrs.get(TextAttribute.SUPERSCRIPT);
|
|
Object oldValue = parentAttrs.get(TextAttribute.SUPERSCRIPT);
|
|
boolean isSuper = false;
|
|
boolean isSub = false;
|
|
if (value != null && !value.equals(oldValue)) {
|
|
isSuper = TextAttribute.SUPERSCRIPT_SUPER.equals(value);
|
|
isSub = TextAttribute.SUPERSCRIPT_SUB.equals(value);
|
|
}
|
|
if (isSuper || isSub) {
|
|
String node = isSuper ? "sup" : "sub";
|
|
sbuffer.append("<");
|
|
sbuffer.append(node);
|
|
sbuffer.append(">");
|
|
sbuffer.append(JRStringUtil.xmlEncode(chunk));
|
|
sbuffer.append("</");
|
|
sbuffer.append(node);
|
|
sbuffer.append(">");
|
|
} else {
|
|
sbuffer.append(JRStringUtil.xmlEncode(chunk));
|
|
}
|
|
}
|
|
|
|
private void parseStyle(JRStyledText styledText, Node parentNode) throws SAXException {
|
|
NodeList nodeList = parentNode.getChildNodes();
|
|
int i;
|
|
label116: for (i = 0; i < nodeList.getLength(); i++) {
|
|
Node node = nodeList.item(i);
|
|
if (node.getNodeType() == 3) {
|
|
styledText.append(node.getNodeValue());
|
|
} else if (node.getNodeType() == 1 && "style".equals(node.getNodeName())) {
|
|
NamedNodeMap nodeAttrs = node.getAttributes();
|
|
Map styleAttrs = new HashMap();
|
|
if (nodeAttrs.getNamedItem("fontName") != null)
|
|
styleAttrs.put(TextAttribute.FAMILY, nodeAttrs.getNamedItem("fontName").getNodeValue());
|
|
if (nodeAttrs.getNamedItem("isBold") != null)
|
|
styleAttrs.put(TextAttribute.WEIGHT, Boolean.valueOf(nodeAttrs.getNamedItem("isBold").getNodeValue()).booleanValue() ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR);
|
|
if (nodeAttrs.getNamedItem("isItalic") != null)
|
|
styleAttrs.put(TextAttribute.POSTURE, Boolean.valueOf(nodeAttrs.getNamedItem("isItalic").getNodeValue()).booleanValue() ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR);
|
|
if (nodeAttrs.getNamedItem("isUnderline") != null)
|
|
styleAttrs.put(TextAttribute.UNDERLINE, Boolean.valueOf(nodeAttrs.getNamedItem("isUnderline").getNodeValue()).booleanValue() ? TextAttribute.UNDERLINE_ON : null);
|
|
if (nodeAttrs.getNamedItem("isStrikeThrough") != null)
|
|
styleAttrs.put(TextAttribute.STRIKETHROUGH, Boolean.valueOf(nodeAttrs.getNamedItem("isStrikeThrough").getNodeValue()).booleanValue() ? TextAttribute.STRIKETHROUGH_ON : null);
|
|
if (nodeAttrs.getNamedItem("size") != null)
|
|
styleAttrs.put(TextAttribute.SIZE, new Float(nodeAttrs.getNamedItem("size").getNodeValue()));
|
|
if (nodeAttrs.getNamedItem("pdfFontName") != null)
|
|
styleAttrs.put(JRTextAttribute.PDF_FONT_NAME, nodeAttrs.getNamedItem("pdfFontName").getNodeValue());
|
|
if (nodeAttrs.getNamedItem("pdfEncoding") != null)
|
|
styleAttrs.put(JRTextAttribute.PDF_ENCODING, nodeAttrs.getNamedItem("pdfEncoding").getNodeValue());
|
|
if (nodeAttrs.getNamedItem("isPdfEmbedded") != null)
|
|
styleAttrs.put(JRTextAttribute.IS_PDF_EMBEDDED, Boolean.valueOf(nodeAttrs.getNamedItem("isPdfEmbedded").getNodeValue()));
|
|
if (nodeAttrs.getNamedItem("forecolor") != null) {
|
|
Color color = JRXmlConstants.getColor(nodeAttrs.getNamedItem("forecolor").getNodeValue(), Color.black);
|
|
styleAttrs.put(TextAttribute.FOREGROUND, color);
|
|
}
|
|
if (nodeAttrs.getNamedItem("backcolor") != null) {
|
|
Color color = JRXmlConstants.getColor(nodeAttrs.getNamedItem("backcolor").getNodeValue(), Color.black);
|
|
styleAttrs.put(TextAttribute.BACKGROUND, color);
|
|
}
|
|
int startIndex = styledText.length();
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
|
|
} else if (node.getNodeType() == 1 && "b".equalsIgnoreCase(node.getNodeName())) {
|
|
Map styleAttrs = new HashMap();
|
|
styleAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
|
|
int startIndex = styledText.length();
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
|
|
} else if (node.getNodeType() == 1 && "i".equalsIgnoreCase(node.getNodeName())) {
|
|
Map styleAttrs = new HashMap();
|
|
styleAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
|
|
int startIndex = styledText.length();
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
|
|
} else if (node.getNodeType() == 1 && "u".equalsIgnoreCase(node.getNodeName())) {
|
|
Map styleAttrs = new HashMap();
|
|
styleAttrs.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
|
int startIndex = styledText.length();
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
|
|
} else if (node.getNodeType() == 1 && "sup".equalsIgnoreCase(node.getNodeName())) {
|
|
Map styleAttrs = new HashMap();
|
|
styleAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
|
|
int startIndex = styledText.length();
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
|
|
} else if (node.getNodeType() == 1 && "sub".equalsIgnoreCase(node.getNodeName())) {
|
|
Map styleAttrs = new HashMap();
|
|
styleAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
|
|
int startIndex = styledText.length();
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
|
|
} else if (node.getNodeType() == 1 && "font".equalsIgnoreCase(node.getNodeName())) {
|
|
NamedNodeMap nodeAttrs = node.getAttributes();
|
|
Map styleAttrs = new HashMap();
|
|
if (nodeAttrs.getNamedItem("face") != null)
|
|
styleAttrs.put(JRTextAttribute.HTML_FONT_FACE, nodeAttrs.getNamedItem("face").getNodeValue());
|
|
if (nodeAttrs.getNamedItem("size") != null)
|
|
styleAttrs.put(TextAttribute.SIZE, new Float(nodeAttrs.getNamedItem("size").getNodeValue()));
|
|
if (nodeAttrs.getNamedItem("color") != null) {
|
|
Color color = JRXmlConstants.getColor(nodeAttrs.getNamedItem("color").getNodeValue(), Color.black);
|
|
styleAttrs.put(TextAttribute.FOREGROUND, color);
|
|
}
|
|
if (nodeAttrs.getNamedItem("face") != null) {
|
|
String[] fontList = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
|
|
String fontFaces = nodeAttrs.getNamedItem("face").getNodeValue();
|
|
StringTokenizer t = new StringTokenizer(fontFaces, ",");
|
|
while (t.hasMoreTokens()) {
|
|
String face = t.nextToken().trim();
|
|
for (int j = 0; j < fontList.length; j++) {
|
|
if (fontList[j].equals(face)) {
|
|
styleAttrs.put(TextAttribute.FAMILY, face);
|
|
continue label116;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
int startIndex = styledText.length();
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
|
|
} else if (node.getNodeType() == 1 && "br".equalsIgnoreCase(node.getNodeName())) {
|
|
styledText.append("\n");
|
|
int startIndex = styledText.length();
|
|
resizeRuns(styledText.getRuns(), startIndex, 1);
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(new HashMap(), startIndex, styledText.length()));
|
|
if (startIndex < styledText.length()) {
|
|
styledText.append("\n");
|
|
resizeRuns(styledText.getRuns(), startIndex, 1);
|
|
}
|
|
} else if (node.getNodeType() == 1 && "li".equalsIgnoreCase(node.getNodeName())) {
|
|
String tmpText = styledText.getText();
|
|
if (tmpText.length() > 0 && !tmpText.endsWith("\n"))
|
|
styledText.append("\n");
|
|
styledText.append(" • ");
|
|
int startIndex = styledText.length();
|
|
resizeRuns(styledText.getRuns(), startIndex, 1);
|
|
parseStyle(styledText, node);
|
|
styledText.addRun(new JRStyledText.Run(new HashMap(), startIndex, styledText.length()));
|
|
Node nextNode = node.getNextSibling();
|
|
String textContent = getFirstTextOccurence(nextNode);
|
|
if (nextNode != null && (nextNode.getNodeType() != 1 || !"li".equalsIgnoreCase(nextNode.getNodeName())) && (textContent == null || !textContent.startsWith("\n"))) {
|
|
styledText.append("\n");
|
|
resizeRuns(styledText.getRuns(), startIndex, 1);
|
|
}
|
|
} else if (node.getNodeType() == 1) {
|
|
String nodeName = "<" + node.getNodeName() + ">";
|
|
throw new SAXException("Tag " + nodeName + " is not a valid styled text tag.");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void resizeRuns(List runs, int startIndex, int count) {
|
|
for (int j = 0; j < runs.size(); j++) {
|
|
JRStyledText.Run run = runs.get(j);
|
|
if (run.startIndex <= startIndex && run.endIndex > startIndex - count)
|
|
run.endIndex += count;
|
|
}
|
|
}
|
|
|
|
private StringBuffer writeStyleAttributes(Map parentAttrs, Map attrs) {
|
|
StringBuffer sbuffer = new StringBuffer();
|
|
Object value = attrs.get(TextAttribute.FAMILY);
|
|
Object oldValue = parentAttrs.get(TextAttribute.FAMILY);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("fontName");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append(value);
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(TextAttribute.WEIGHT);
|
|
oldValue = parentAttrs.get(TextAttribute.WEIGHT);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("isBold");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append(value.equals(TextAttribute.WEIGHT_BOLD));
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(TextAttribute.POSTURE);
|
|
oldValue = parentAttrs.get(TextAttribute.POSTURE);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("isItalic");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append(value.equals(TextAttribute.POSTURE_OBLIQUE));
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(TextAttribute.UNDERLINE);
|
|
oldValue = parentAttrs.get(TextAttribute.UNDERLINE);
|
|
if ((value == null && oldValue != null) || (value != null && !value.equals(oldValue))) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("isUnderline");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append((value != null));
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(TextAttribute.STRIKETHROUGH);
|
|
oldValue = parentAttrs.get(TextAttribute.STRIKETHROUGH);
|
|
if ((value == null && oldValue != null) || (value != null && !value.equals(oldValue))) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("isStrikeThrough");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append((value != null));
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(TextAttribute.SIZE);
|
|
oldValue = parentAttrs.get(TextAttribute.SIZE);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("size");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append(((Float)value).intValue());
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(JRTextAttribute.PDF_FONT_NAME);
|
|
oldValue = parentAttrs.get(JRTextAttribute.PDF_FONT_NAME);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("pdfFontName");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append(value);
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(JRTextAttribute.PDF_ENCODING);
|
|
oldValue = parentAttrs.get(JRTextAttribute.PDF_ENCODING);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("pdfEncoding");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append(value);
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(JRTextAttribute.IS_PDF_EMBEDDED);
|
|
oldValue = parentAttrs.get(JRTextAttribute.IS_PDF_EMBEDDED);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("isPdfEmbedded");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append(value);
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(TextAttribute.FOREGROUND);
|
|
oldValue = parentAttrs.get(TextAttribute.FOREGROUND);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("forecolor");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append("#");
|
|
sbuffer.append(JRColorUtil.getColorHexa((Color)value));
|
|
sbuffer.append("\"");
|
|
}
|
|
value = attrs.get(TextAttribute.BACKGROUND);
|
|
oldValue = parentAttrs.get(TextAttribute.BACKGROUND);
|
|
if (value != null && !value.equals(oldValue)) {
|
|
sbuffer.append(" ");
|
|
sbuffer.append("backcolor");
|
|
sbuffer.append("=\"");
|
|
sbuffer.append("#");
|
|
sbuffer.append(JRColorUtil.getColorHexa((Color)value));
|
|
sbuffer.append("\"");
|
|
}
|
|
return sbuffer;
|
|
}
|
|
|
|
private String getFirstTextOccurence(Node node) {
|
|
if (node != null) {
|
|
if (node.getNodeValue() != null)
|
|
return node.getNodeValue();
|
|
NodeList nodeList = node.getChildNodes();
|
|
for (int i = 0; i < nodeList.getLength(); i++) {
|
|
String firstOccurence = getFirstTextOccurence(nodeList.item(i));
|
|
if (firstOccurence != null)
|
|
return firstOccurence;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void error(SAXParseException e) {}
|
|
|
|
public void fatalError(SAXParseException e) {}
|
|
|
|
public void warning(SAXParseException e) {}
|
|
}
|