package com.xenoage.zong.core.text; import static com.xenoage.utils.collections.CList.clist; import static com.xenoage.utils.collections.CList.ilist; import static com.xenoage.zong.core.text.FormattedTextParagraph.fPara; import static com.xenoage.zong.core.text.FormattedTextString.fString; import lombok.EqualsAndHashCode; import lombok.Getter; import com.xenoage.utils.annotations.Const; import com.xenoage.utils.collections.CList; import com.xenoage.utils.collections.IList; import com.xenoage.utils.math.geom.Rectangle2f; /** * Class for formatted text. * * A formatted text contains a list of paragraphs * with multi-styled text. * * @author Andreas Wenger */ @Const @EqualsAndHashCode public class FormattedText implements Text { /** An empty text. */ public static final FormattedText empty = fText(); /** The list of paragraphs */ @Getter private final IList<FormattedTextParagraph> paragraphs; //cache private transient float width = Float.NaN; private transient float height = Float.NaN; /** * Creates a {@link FormattedText} with the given paragraphs. */ public FormattedText(IList<FormattedTextParagraph> paragraphs) { this.paragraphs = paragraphs; } /** * Creates an empty {@link FormattedText}. */ public static FormattedText fText() { return new FormattedText(CList.<FormattedTextParagraph>ilist()); } /** * Creates a {@link FormattedText} with the given paragraphs. */ public static FormattedText fText(IList<FormattedTextParagraph> paragraphs) { return new FormattedText(paragraphs); } /** * Creates a {@link FormattedText} with the given paragraph. */ public static FormattedText fText(FormattedTextParagraph paragraph) { return new FormattedText(ilist(paragraph)); } /** * Creates a {@link FormattedText} with the given text, using * the given style and alignment. Paragraphs are divided by "\n". */ public static FormattedText fText(String text, FormattedTextStyle style, Alignment alignment) { String[] lines = text.split("\\n"); CList<FormattedTextParagraph> paras = clist(); for (String line : lines) paras.add(fPara(fString(line, style), alignment)); return fText(paras); } /** * Breaks this formatted text up so that it fits into the given width * and returns the result. * The result is no deep copy of the whole text, instead references to the * unmodified parts are used. */ public FormattedText lineBreak(float width) { CList<FormattedTextParagraph> ret = clist(); //break up paragraphs for (FormattedTextParagraph paragraph : paragraphs) { for (FormattedTextParagraph paragraphLine : paragraph.lineBreak(width)) { ret.add(paragraphLine); } } return new FormattedText(ret.close()); } @Override public String toString() { StringBuilder ret = new StringBuilder(); for (FormattedTextParagraph paragraph : paragraphs) { ret.append(paragraph.toString()); ret.append('\n'); } ret.delete(ret.length() - 1, ret.length()); //remove last '\n' return ret.toString(); } /** * Gets the width of this text in mm (without automatic line breaks). */ public float getWidth() { if (Float.isNaN(width)) { float maxWidth = 0; for (FormattedTextParagraph paragraph : paragraphs) { maxWidth = Math.max(maxWidth, paragraph.getMetrics().getWidth()); } width = maxWidth; } return width; } /** * Gets the height of this text in mm (without automatic line breaks). */ public float getHeight() { if (Float.isNaN(height)) { float sumHeight = 0; for (FormattedTextParagraph paragraph : paragraphs) { sumHeight += paragraph.getHeightMm(); } height = sumHeight; } return height; } /** * Gets the bounding rectangle of the text. */ public Rectangle2f getBoundingRect() { float w = getWidth(); float h = getHeight(); float x = 0; FormattedTextParagraph p = getFirstParagraph(); switch (p.getAlignment()) { case Center: x -= w / 2; break; case Right: x -= w; break; default: break; } float y = -1 * p.getMetrics().getAscent(); return new Rectangle2f(x, y, w, h); } /** * Gets the first paragraph, or throws an {@link IndexOutOfBoundsException} * if there is none. */ public FormattedTextParagraph getFirstParagraph() { return paragraphs.getFirst(); } @Override public int getLength() { int ret = 0; for (FormattedTextParagraph paragraph : paragraphs) { ret += paragraph.getLength(); } ret += (paragraphs.size() - 1); //\n as paragraph separator return ret; } }