|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package javax.swing.text.html; |
|
|
|
import java.io.Writer; |
|
import java.io.IOException; |
|
import java.util.*; |
|
import java.awt.Color; |
|
import javax.swing.text.*; |
|
|
|
/** |
|
* MinimalHTMLWriter is a fallback writer used by the |
|
* HTMLEditorKit to write out HTML for a document that |
|
* is a not produced by the EditorKit. |
|
* |
|
* The format for the document is: |
|
* <pre> |
|
* <html> |
|
* <head> |
|
* <style> |
|
* <!-- list of named styles |
|
* p.normal { |
|
* font-family: SansSerif; |
|
* margin-height: 0; |
|
* font-size: 14 |
|
* } |
|
* --> |
|
* </style> |
|
* </head> |
|
* <body> |
|
* <p style=normal> |
|
* <b>Bold, italic, and underline attributes |
|
* of the run are emitted as HTML tags. |
|
* The remaining attributes are emitted as |
|
* part of the style attribute of a <span> tag. |
|
* The syntax is similar to inline styles.</b> |
|
* </p> |
|
* </body> |
|
* </html> |
|
* </pre> |
|
* |
|
* @author Sunita Mani |
|
*/ |
|
|
|
public class MinimalHTMLWriter extends AbstractWriter { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final int BOLD = 0x01; |
|
private static final int ITALIC = 0x02; |
|
private static final int UNDERLINE = 0x04; |
|
|
|
|
|
private static final CSS css = new CSS(); |
|
|
|
private int fontMask = 0; |
|
|
|
int startOffset = 0; |
|
int endOffset = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private AttributeSet fontAttributes; |
|
|
|
|
|
|
|
|
|
*/ |
|
private Hashtable<String, String> styleNameMapping; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MinimalHTMLWriter(Writer w, StyledDocument doc) { |
|
super(w, doc); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len) { |
|
super(w, doc, pos, len); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void write() throws IOException, BadLocationException { |
|
styleNameMapping = new Hashtable<String, String>(); |
|
writeStartTag("<html>"); |
|
writeHeader(); |
|
writeBody(); |
|
writeEndTag("</html>"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeAttributes(AttributeSet attr) throws IOException { |
|
Enumeration attributeNames = attr.getAttributeNames(); |
|
while (attributeNames.hasMoreElements()) { |
|
Object name = attributeNames.nextElement(); |
|
if ((name instanceof StyleConstants.ParagraphConstants) || |
|
(name instanceof StyleConstants.CharacterConstants) || |
|
(name instanceof StyleConstants.FontConstants) || |
|
(name instanceof StyleConstants.ColorConstants)) { |
|
indent(); |
|
write(name.toString()); |
|
write(':'); |
|
write(css.styleConstantsValueToCSSValue |
|
((StyleConstants)name, attr.getAttribute(name)). |
|
toString()); |
|
write(';'); |
|
write(NEWLINE); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void text(Element elem) throws IOException, BadLocationException { |
|
String contentStr = getText(elem); |
|
if ((contentStr.length() > 0) && |
|
(contentStr.charAt(contentStr.length()-1) == NEWLINE)) { |
|
contentStr = contentStr.substring(0, contentStr.length()-1); |
|
} |
|
if (contentStr.length() > 0) { |
|
write(contentStr); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeStartTag(String tag) throws IOException { |
|
indent(); |
|
write(tag); |
|
write(NEWLINE); |
|
incrIndent(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeEndTag(String endTag) throws IOException { |
|
decrIndent(); |
|
indent(); |
|
write(endTag); |
|
write(NEWLINE); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeHeader() throws IOException { |
|
writeStartTag("<head>"); |
|
writeStartTag("<style>"); |
|
writeStartTag("<!--"); |
|
writeStyles(); |
|
writeEndTag("-->"); |
|
writeEndTag("</style>"); |
|
writeEndTag("</head>"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeStyles() throws IOException { |
|
|
|
|
|
|
|
|
|
*/ |
|
DefaultStyledDocument styledDoc = ((DefaultStyledDocument)getDocument()); |
|
Enumeration styleNames = styledDoc.getStyleNames(); |
|
|
|
while (styleNames.hasMoreElements()) { |
|
Style s = styledDoc.getStyle((String)styleNames.nextElement()); |
|
|
|
|
|
from the list we check check for 0. **/ |
|
if (s.getAttributeCount() == 1 && |
|
s.isDefined(StyleConstants.NameAttribute)) { |
|
continue; |
|
} |
|
indent(); |
|
write("p." + addStyleName(s.getName())); |
|
write(" {\n"); |
|
incrIndent(); |
|
writeAttributes(s); |
|
decrIndent(); |
|
indent(); |
|
write("}\n"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeBody() throws IOException, BadLocationException { |
|
ElementIterator it = getElementIterator(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
it.current(); |
|
|
|
Element next; |
|
|
|
writeStartTag("<body>"); |
|
|
|
boolean inContent = false; |
|
|
|
while((next = it.next()) != null) { |
|
if (!inRange(next)) { |
|
continue; |
|
} |
|
if (next instanceof AbstractDocument.BranchElement) { |
|
if (inContent) { |
|
writeEndParagraph(); |
|
inContent = false; |
|
fontMask = 0; |
|
} |
|
writeStartParagraph(next); |
|
} else if (isText(next)) { |
|
writeContent(next, !inContent); |
|
inContent = true; |
|
} else { |
|
writeLeaf(next); |
|
inContent = true; |
|
} |
|
} |
|
if (inContent) { |
|
writeEndParagraph(); |
|
} |
|
writeEndTag("</body>"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeEndParagraph() throws IOException { |
|
writeEndMask(fontMask); |
|
if (inFontTag()) { |
|
endSpanTag(); |
|
} else { |
|
write(NEWLINE); |
|
} |
|
writeEndTag("</p>"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeStartParagraph(Element elem) throws IOException { |
|
AttributeSet attr = elem.getAttributes(); |
|
Object resolveAttr = attr.getAttribute(StyleConstants.ResolveAttribute); |
|
if (resolveAttr instanceof StyleContext.NamedStyle) { |
|
writeStartTag("<p class=" + mapStyleName(((StyleContext.NamedStyle)resolveAttr).getName()) + ">"); |
|
} else { |
|
writeStartTag("<p>"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeLeaf(Element elem) throws IOException { |
|
indent(); |
|
if (elem.getName() == StyleConstants.IconElementName) { |
|
writeImage(elem); |
|
} else if (elem.getName() == StyleConstants.ComponentElementName) { |
|
writeComponent(elem); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeImage(Element elem) throws IOException { |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeComponent(Element elem) throws IOException { |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean isText(Element elem) { |
|
return (elem.getName() == AbstractDocument.ContentElementName); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeContent(Element elem, boolean needsIndenting) |
|
throws IOException, BadLocationException { |
|
|
|
AttributeSet attr = elem.getAttributes(); |
|
writeNonHTMLAttributes(attr); |
|
if (needsIndenting) { |
|
indent(); |
|
} |
|
writeHTMLTags(attr); |
|
text(elem); |
|
} |
|
|
|
|
|
/** |
|
* Generates |
|
* bold <b>, italic <i>, and <u> tags for the |
|
* text based on its attribute settings. |
|
* |
|
* @exception IOException on any I/O error |
|
*/ |
|
|
|
protected void writeHTMLTags(AttributeSet attr) throws IOException { |
|
|
|
int oldMask = fontMask; |
|
setFontMask(attr); |
|
|
|
int endMask = 0; |
|
int startMask = 0; |
|
if ((oldMask & BOLD) != 0) { |
|
if ((fontMask & BOLD) == 0) { |
|
endMask |= BOLD; |
|
} |
|
} else if ((fontMask & BOLD) != 0) { |
|
startMask |= BOLD; |
|
} |
|
|
|
if ((oldMask & ITALIC) != 0) { |
|
if ((fontMask & ITALIC) == 0) { |
|
endMask |= ITALIC; |
|
} |
|
} else if ((fontMask & ITALIC) != 0) { |
|
startMask |= ITALIC; |
|
} |
|
|
|
if ((oldMask & UNDERLINE) != 0) { |
|
if ((fontMask & UNDERLINE) == 0) { |
|
endMask |= UNDERLINE; |
|
} |
|
} else if ((fontMask & UNDERLINE) != 0) { |
|
startMask |= UNDERLINE; |
|
} |
|
writeEndMask(endMask); |
|
writeStartMask(startMask); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void setFontMask(AttributeSet attr) { |
|
if (StyleConstants.isBold(attr)) { |
|
fontMask |= BOLD; |
|
} |
|
|
|
if (StyleConstants.isItalic(attr)) { |
|
fontMask |= ITALIC; |
|
} |
|
|
|
if (StyleConstants.isUnderline(attr)) { |
|
fontMask |= UNDERLINE; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeStartMask(int mask) throws IOException { |
|
if (mask != 0) { |
|
if ((mask & UNDERLINE) != 0) { |
|
write("<u>"); |
|
} |
|
if ((mask & ITALIC) != 0) { |
|
write("<i>"); |
|
} |
|
if ((mask & BOLD) != 0) { |
|
write("<b>"); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeEndMask(int mask) throws IOException { |
|
if (mask != 0) { |
|
if ((mask & BOLD) != 0) { |
|
write("</b>"); |
|
} |
|
if ((mask & ITALIC) != 0) { |
|
write("</i>"); |
|
} |
|
if ((mask & UNDERLINE) != 0) { |
|
write("</u>"); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException { |
|
|
|
String style = ""; |
|
String separator = "; "; |
|
|
|
if (inFontTag() && fontAttributes.isEqual(attr)) { |
|
return; |
|
} |
|
|
|
boolean first = true; |
|
Color color = (Color)attr.getAttribute(StyleConstants.Foreground); |
|
if (color != null) { |
|
style += "color: " + css.styleConstantsValueToCSSValue |
|
((StyleConstants)StyleConstants.Foreground, |
|
color); |
|
first = false; |
|
} |
|
Integer size = (Integer)attr.getAttribute(StyleConstants.FontSize); |
|
if (size != null) { |
|
if (!first) { |
|
style += separator; |
|
} |
|
style += "font-size: " + size.intValue() + "pt"; |
|
first = false; |
|
} |
|
|
|
String family = (String)attr.getAttribute(StyleConstants.FontFamily); |
|
if (family != null) { |
|
if (!first) { |
|
style += separator; |
|
} |
|
style += "font-family: " + family; |
|
first = false; |
|
} |
|
|
|
if (style.length() > 0) { |
|
if (fontMask != 0) { |
|
writeEndMask(fontMask); |
|
fontMask = 0; |
|
} |
|
startSpanTag(style); |
|
fontAttributes = attr; |
|
} |
|
else if (fontAttributes != null) { |
|
writeEndMask(fontMask); |
|
fontMask = 0; |
|
endSpanTag(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean inFontTag() { |
|
return (fontAttributes != null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void endFontTag() throws IOException { |
|
write(NEWLINE); |
|
writeEndTag("</font>"); |
|
fontAttributes = null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void startFontTag(String style) throws IOException { |
|
boolean callIndent = false; |
|
if (inFontTag()) { |
|
endFontTag(); |
|
callIndent = true; |
|
} |
|
writeStartTag("<font style=\"" + style + "\">"); |
|
if (callIndent) { |
|
indent(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void startSpanTag(String style) throws IOException { |
|
boolean callIndent = false; |
|
if (inFontTag()) { |
|
endSpanTag(); |
|
callIndent = true; |
|
} |
|
writeStartTag("<span style=\"" + style + "\">"); |
|
if (callIndent) { |
|
indent(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void endSpanTag() throws IOException { |
|
write(NEWLINE); |
|
writeEndTag("</span>"); |
|
fontAttributes = null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private String addStyleName(String style) { |
|
if (styleNameMapping == null) { |
|
return style; |
|
} |
|
StringBuilder sb = null; |
|
for (int counter = style.length() - 1; counter >= 0; counter--) { |
|
if (!isValidCharacter(style.charAt(counter))) { |
|
if (sb == null) { |
|
sb = new StringBuilder(style); |
|
} |
|
sb.setCharAt(counter, 'a'); |
|
} |
|
} |
|
String mappedName = (sb != null) ? sb.toString() : style; |
|
while (styleNameMapping.get(mappedName) != null) { |
|
mappedName = mappedName + 'x'; |
|
} |
|
styleNameMapping.put(style, mappedName); |
|
return mappedName; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private String mapStyleName(String style) { |
|
if (styleNameMapping == null) { |
|
return style; |
|
} |
|
String retValue = styleNameMapping.get(style); |
|
return (retValue == null) ? style : retValue; |
|
} |
|
|
|
private boolean isValidCharacter(char character) { |
|
return ((character >= 'a' && character <= 'z') || |
|
(character >= 'A' && character <= 'Z')); |
|
} |
|
} |