package com.xenoage.zong.desktop.utils.text;
import static com.xenoage.utils.collections.CList.clist;
import static com.xenoage.utils.jse.color.AwtColorUtils.fromAwtColor;
import static com.xenoage.utils.jse.color.AwtColorUtils.toAwtColor;
import static com.xenoage.utils.jse.font.AwtFontUtils.toAwtFont;
import static com.xenoage.zong.desktop.utils.text.AlignmentUtils.applyAlignmentToAttributeSet;
import static com.xenoage.zong.desktop.utils.text.AlignmentUtils.fromAttributeSet;
import java.awt.Font;
import java.awt.font.TextLayout;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import com.xenoage.utils.collections.CList;
import com.xenoage.utils.font.FontInfo;
import com.xenoage.utils.font.FontStyle;
import com.xenoage.zong.core.text.Alignment;
import com.xenoage.zong.core.text.FormattedText;
import com.xenoage.zong.core.text.FormattedTextElement;
import com.xenoage.zong.core.text.FormattedTextParagraph;
import com.xenoage.zong.core.text.FormattedTextString;
import com.xenoage.zong.core.text.FormattedTextStyle;
import com.xenoage.zong.core.text.Superscript;
/**
* This class converts a {@link StyledDocument} or a {@link TextLayout}
* into a {@link FormattedText} or the other way round.
*
* @author Andreas Wenger
*/
public class FormattedTextConverter {
/**
* Creates a {@link FormattedText} from the given {@link StyledDocument}.
*/
public static FormattedText fromStyledDocument(StyledDocument styledDoc) {
CList<FormattedTextParagraph> paragraphs = clist();
int endPos = styledDoc.getLength();
AttributeSet attribute = null;
String textelement = "";
String letter = "";
CList<FormattedTextElement> elements = clist();
Alignment alignment = null;
//iterate through the letters. If the style has changed, create a new
//element, otherwise just attach it to the old one, and create a new
//paragraph, when '\n' is found
for (int i = 0; i < endPos; i++) {
try {
letter = styledDoc.getText(i, 1);
} catch (BadLocationException e) {
}
//line break: create new paragraph
if (letter.equals("\n")) {
if (!textelement.equals("")) {
elements.add(new FormattedTextString(textelement,
getStyleFromAttributeSet(attribute)));
textelement = "";
}
paragraphs.add(new FormattedTextParagraph(elements, alignment));
elements = clist();
alignment = null;
}
else if (attribute != styledDoc.getCharacterElement(i).getAttributes()) {
//set alignment, if not already done
if (alignment == null) {
alignment = fromAttributeSet(styledDoc.getParagraphElement(i).getAttributes());
}
//style has changed, so save old element and create a new one
if (!textelement.equals("")) {
elements.add(new FormattedTextString(textelement,
getStyleFromAttributeSet(attribute)));
}
attribute = styledDoc.getCharacterElement(i).getAttributes();
textelement = letter;
}
else {
//style stayed the same, so just append
textelement += letter;
}
}
if (!textelement.equals("")) {
//save the last string
elements.add(new FormattedTextString(textelement,
getStyleFromAttributeSet(attribute)));
}
if (elements.size() > 0) {
//add (non-empty) paragraph
paragraphs.add(new FormattedTextParagraph(elements, alignment));
}
return new FormattedText(paragraphs);
}
/**
* Creates a {@link StyledDocument} from the given {@link FormattedText}.
*/
public static StyledDocument toStyledDocument(FormattedText input) {
StyledDocument styledDoc = new DefaultStyledDocument();
//handle all paragraphs
SimpleAttributeSet lastStyle = new SimpleAttributeSet();
int line = 0;
for (FormattedTextParagraph p : input.getParagraphs()) {
//apply the alignment of the paragraph (apply style to the first letter of the paragraph)
SimpleAttributeSet paragraphStyle = new SimpleAttributeSet();
applyAlignmentToAttributeSet(p.getAlignment(), paragraphStyle);
styledDoc.setParagraphAttributes(styledDoc.getLength(), 1, paragraphStyle, true);
//handle all elements within this paragraph
for (FormattedTextElement t : p.getElements()) //TODO: Symbols ?!
{
if (t instanceof FormattedTextString) {
FormattedTextString s = (FormattedTextString) t;
try {
lastStyle = getAttributeSetFromStyle(s.getStyle());
styledDoc.insertString(styledDoc.getLength(), s.getText(), lastStyle);
} catch (BadLocationException e) {
}
}
}
//after the paragraph (not after the last): insert a line break
if (line < input.getParagraphs().size() - 1) {
try {
styledDoc.insertString(styledDoc.getLength(), "\n", lastStyle);
} catch (BadLocationException e) {
}
}
line++;
}
return styledDoc;
}
private static FormattedTextStyle getStyleFromAttributeSet(AttributeSet attr) {
if (attr == null) {
return FormattedTextStyle.defaultStyle;
}
//font style
FontStyle fontStyle = FontStyle.normal;
if (StyleConstants.isBold(attr)) {
fontStyle = fontStyle.with(FontStyle.Bold);
}
if (StyleConstants.isItalic(attr)) {
fontStyle = fontStyle.with(FontStyle.Italic);
}
if (StyleConstants.isUnderline(attr)) {
fontStyle = fontStyle.with(FontStyle.Underline);
}
if (StyleConstants.isStrikeThrough(attr)) {
fontStyle = fontStyle.with(FontStyle.Strikethrough);
}
//superscript
Superscript superscript = Superscript.Normal;
if (StyleConstants.isSuperscript(attr)) {
superscript = Superscript.Super;
}
if (StyleConstants.isSubscript(attr)) {
superscript = Superscript.Sub;
}
//font
FontInfo fontInfo = new FontInfo(StyleConstants.getFontFamily(attr),
(float) StyleConstants.getFontSize(attr), fontStyle);
return new FormattedTextStyle(fontInfo, fromAwtColor(StyleConstants.getForeground(attr)), superscript);
}
private static SimpleAttributeSet getAttributeSetFromStyle(FormattedTextStyle style) {
SimpleAttributeSet attr = new SimpleAttributeSet();
if (style == null) {
return attr;
}
//font style
FontStyle fontStyle = style.getFont().getStyle();
StyleConstants.setBold(attr, fontStyle.isSet(FontStyle.Bold));
StyleConstants.setItalic(attr, fontStyle.isSet(FontStyle.Italic));
StyleConstants.setUnderline(attr, fontStyle.isSet(FontStyle.Underline));
StyleConstants.setStrikeThrough(attr, fontStyle.isSet(FontStyle.Strikethrough));
//color
StyleConstants.setForeground(attr, toAwtColor(style.getColor()));
//font
Font font = toAwtFont(style.getFont());
StyleConstants.setFontFamily(attr, font.getFamily());
StyleConstants.setFontSize(attr, font.getSize());
//superscript
if (style.getSuperscript() == Superscript.Super) {
StyleConstants.setSuperscript(attr, true);
}
else if (style.getSuperscript() == Superscript.Sub) {
StyleConstants.setSubscript(attr, true);
}
return attr;
}
}