net.sf.jasperreports.engine.util.JEditorPaneHtmlMarkupProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jasperreports Show documentation
Show all versions of jasperreports Show documentation
Free Java Reporting Library
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see .
*/
package net.sf.jasperreports.engine.util;
import java.awt.font.TextAttribute;
import java.text.AttributedCharacterIterator.Attribute;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import javax.swing.JEditorPane;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AbstractDocument.LeafElement;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTML.Tag;
import javax.swing.text.html.HTMLDocument.RunElement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sf.jasperreports.engine.JRPrintHyperlink;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.base.JRBasePrintHyperlink;
import net.sf.jasperreports.engine.type.HyperlinkTypeEnum;
/**
* @author Teodor Danciu ([email protected])
*/
public class JEditorPaneHtmlMarkupProcessor extends JEditorPaneMarkupProcessor
{
private static final Log log = LogFactory.getLog(JEditorPaneHtmlMarkupProcessor.class);
public static final String DEFAULT_BULLET_CHARACTER = "\u2022";
public static final String DEFAULT_BULLET_SEPARATOR = ".";
private static JEditorPaneHtmlMarkupProcessor instance;
/**
*
*/
public static JEditorPaneHtmlMarkupProcessor getInstance()
{
if (instance == null)
{
instance = new JEditorPaneHtmlMarkupProcessor();
}
return instance;
}
@Override
public String convert(String srcText)
{
JEditorPane editorPane = new JEditorPane("text/html", srcText);
editorPane.setEditable(false);
List elements = new ArrayList();
Document document = editorPane.getDocument();
Element root = document.getDefaultRootElement();
if (root != null)
{
addElements(elements, root);
}
int startOffset = 0;
int endOffset = 0;
int crtOffset = 0;
String chunk = null;
JRPrintHyperlink hyperlink = null;
Element element = null;
Element parent = null;
boolean bodyOccurred = false;
int[] orderedListIndex = new int[elements.size()];
String whitespace = " ";
String[] whitespaces = new String[elements.size()];
for(int i = 0; i < elements.size(); i++)
{
whitespaces[i] = "";
}
StringBuilder text = new StringBuilder();
List styleRuns = new ArrayList<>();
for(int i = 0; i < elements.size(); i++)
{
if (bodyOccurred && chunk != null)
{
text.append(chunk);
Map styleAttributes = getAttributes(element.getAttributes());
if (hyperlink != null)
{
styleAttributes.put(JRTextAttribute.HYPERLINK, hyperlink);
hyperlink = null;
}
if (!styleAttributes.isEmpty())
{
styleRuns.add(new JRStyledText.Run(styleAttributes,
startOffset + crtOffset, endOffset + crtOffset));
}
}
chunk = null;
element = elements.get(i);
parent = element.getParentElement();
startOffset = element.getStartOffset();
endOffset = element.getEndOffset();
AttributeSet attrs = element.getAttributes();
Object elementName = attrs.getAttribute(AbstractDocument.ElementNameAttribute);
Object object = (elementName != null) ? null : attrs.getAttribute(StyleConstants.NameAttribute);
if (object instanceof HTML.Tag)
{
HTML.Tag htmlTag = (HTML.Tag) object;
if(htmlTag == Tag.BODY)
{
bodyOccurred = true;
crtOffset = - startOffset;
}
else if(htmlTag == Tag.BR)
{
chunk = "\n";
}
else if(htmlTag == Tag.OL)
{
orderedListIndex[i] = 0;
String parentName = parent.getName().toLowerCase();
whitespaces[i] = whitespaces[elements.indexOf(parent)] + whitespace;
if(parentName.equals("li"))
{
chunk = "";
}
else
{
chunk = "\n";
++crtOffset;
}
}
else if(htmlTag == Tag.UL)
{
whitespaces[i] = whitespaces[elements.indexOf(parent)] + whitespace;
String parentName = parent.getName().toLowerCase();
if(parentName.equals("li"))
{
chunk = "";
}
else
{
chunk = "\n";
++crtOffset;
}
}
else if(htmlTag == Tag.LI)
{
whitespaces[i] = whitespaces[elements.indexOf(parent)];
if(element.getElement(0) != null &&
(element.getElement(0).getName().toLowerCase().equals("ol") || element.getElement(0).getName().toLowerCase().equals("ul"))
)
{
chunk = "";
}
else if(parent.getName().equals("ol"))
{
int index = elements.indexOf(parent);
Object type = parent.getAttributes().getAttribute(HTML.Attribute.TYPE);
Object startObject = parent.getAttributes().getAttribute(HTML.Attribute.START);
int start = startObject == null ? 0 : Math.max(0, Integer.valueOf(startObject.toString()) - 1);
String suffix = "";
++orderedListIndex[index];
if(type !=null)
{
switch(((String)type).charAt(0))
{
case 'A':
suffix = getOLBulletChars(orderedListIndex[index] + start, true);
break;
case 'a':
suffix = getOLBulletChars(orderedListIndex[index] + start, false);
break;
case 'I':
suffix = JRStringUtil.getRomanNumeral(orderedListIndex[index] + start, true);
break;
case 'i':
suffix = JRStringUtil.getRomanNumeral(orderedListIndex[index] + start, false);
break;
case '1':
default:
suffix = String.valueOf(orderedListIndex[index] + start);
break;
}
}
else
{
suffix += orderedListIndex[index] + start;
}
chunk = whitespaces[index] + suffix + DEFAULT_BULLET_SEPARATOR + " ";
}
else
{
chunk = whitespaces[elements.indexOf(parent)] + DEFAULT_BULLET_CHARACTER + " ";
}
crtOffset += chunk.length();
}
else if (element instanceof LeafElement)
{
if (element instanceof RunElement)
{
RunElement runElement = (RunElement)element;
AttributeSet attrSet = (AttributeSet)runElement.getAttribute(Tag.A);
if (attrSet != null)
{
hyperlink = new JRBasePrintHyperlink();
hyperlink.setHyperlinkType(HyperlinkTypeEnum.REFERENCE);
hyperlink.setHyperlinkReference((String)attrSet.getAttribute(HTML.Attribute.HREF));
hyperlink.setLinkTarget((String)attrSet.getAttribute(HTML.Attribute.TARGET));
}
}
try
{
chunk = document.getText(startOffset, endOffset - startOffset);
}
catch(BadLocationException e)
{
if (log.isDebugEnabled())
{
log.debug("Error converting markup.", e);
}
}
}
}
}
if (chunk != null)
{
if (!"\n".equals(chunk))
{
text.append(chunk);
Map styleAttributes = getAttributes(element.getAttributes());
if (hyperlink != null)
{
styleAttributes.put(JRTextAttribute.HYPERLINK, hyperlink);
hyperlink = null;
}
if (!styleAttributes.isEmpty())
{
styleRuns.add(new JRStyledText.Run(styleAttributes,
startOffset + crtOffset, endOffset + crtOffset));
}
}
else
{
//final newline, not appending
//check if there's any style run that would have covered it, that can happen if there's a tag with style
int length = text.length();
for (ListIterator it = styleRuns.listIterator(); it.hasNext();)
{
JRStyledText.Run run = it.next();
//only looking at runs that end at the position where the newline should have been
//we don't want to hide bugs in which runs that span after the text length are created
if (run.endIndex == length + 1)
{
if (run.startIndex < run.endIndex - 1)
{
it.set(new JRStyledText.Run(run.attributes, run.startIndex, run.endIndex - 1));
}
else
{
it.remove();
}
}
}
}
}
JRStyledText styledText = new JRStyledText(null, text.toString());
for (JRStyledText.Run run : styleRuns)
{
styledText.addRun(run);
}
styledText.setGlobalAttributes(new HashMap());
return JRStyledTextParser.getInstance().write(styledText);
}
/**
*
*/
protected void addElements(List elements, Element element)
{
//if(element instanceof LeafElement)
{
elements.add(element);
}
for(int i = 0; i < element.getElementCount(); i++)
{
Element child = element.getElement(i);
addElements(elements, child);
}
}
@Override
protected Map getAttributes(AttributeSet attrSet)
{
Map attrMap = new HashMap();
if (attrSet.isDefined(StyleConstants.FontFamily))
{
attrMap.put(
TextAttribute.FAMILY,
StyleConstants.getFontFamily(attrSet)
);
}
if (attrSet.isDefined(StyleConstants.Bold))
{
attrMap.put(
TextAttribute.WEIGHT,
StyleConstants.isBold(attrSet) ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR
);
}
if (attrSet.isDefined(StyleConstants.Italic))
{
attrMap.put(
TextAttribute.POSTURE,
StyleConstants.isItalic(attrSet) ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR
);
}
if (attrSet.isDefined(StyleConstants.Underline))
{
attrMap.put(
TextAttribute.UNDERLINE,
StyleConstants.isUnderline(attrSet) ? TextAttribute.UNDERLINE_ON : null
);
}
if (attrSet.isDefined(StyleConstants.StrikeThrough))
{
attrMap.put(
TextAttribute.STRIKETHROUGH,
StyleConstants.isStrikeThrough(attrSet) ? TextAttribute.STRIKETHROUGH_ON : null
);
}
if (attrSet.isDefined(StyleConstants.FontSize))
{
attrMap.put(
TextAttribute.SIZE,
StyleConstants.getFontSize(attrSet)
);
}
if (attrSet.isDefined(StyleConstants.Foreground))
{
attrMap.put(
TextAttribute.FOREGROUND,
StyleConstants.getForeground(attrSet)
);
}
if (attrSet.isDefined(StyleConstants.Background))
{
attrMap.put(
TextAttribute.BACKGROUND,
StyleConstants.getBackground(attrSet)
);
}
//FIXME: why StyleConstants.isSuperscript(attrSet) does return false
if (attrSet.isDefined(StyleConstants.Superscript) && !StyleConstants.isSubscript(attrSet))
{
attrMap.put(
TextAttribute.SUPERSCRIPT,
TextAttribute.SUPERSCRIPT_SUPER
);
}
if (attrSet.isDefined(StyleConstants.Subscript) && StyleConstants.isSubscript(attrSet))
{
attrMap.put(
TextAttribute.SUPERSCRIPT,
TextAttribute.SUPERSCRIPT_SUB
);
}
return attrMap;
}
/**
*
* @param index the current index between 0 and 18277
* @param isUpperCase specifies whether the result should be made of upper case characters
* @return a character representation of the numeric index in an ordered bullet list, that contains up to 3 chars
*/
protected static String getOLBulletChars(int index, boolean isUpperCase)
{
// max 3-letter index is 18277
if(index < 0 || index > 18277)
{
throw
new JRRuntimeException(
JRStringUtil.EXCEPTION_MESSAGE_KEY_NUMBER_OUTSIDE_BOUNDS,
new Object[]{index});
}
return JRStringUtil.getLetterNumeral(index, isUpperCase);
}
}