package com.xenoage.zong.io.musicxml.in;
import static com.xenoage.utils.collections.CList.clist;
import static com.xenoage.utils.iterators.It.it;
import static com.xenoage.zong.io.musicxml.in.readers.OtherReader.readAlignment;
import com.xenoage.utils.collections.CList;
import com.xenoage.utils.color.Color;
import com.xenoage.utils.font.FontInfo;
import com.xenoage.utils.math.geom.Point2f;
import com.xenoage.utils.math.geom.Size2f;
import com.xenoage.zong.core.format.ScoreFormat;
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.io.musicxml.in.readers.FontInfoReader;
import com.xenoage.zong.layout.Layout;
import com.xenoage.zong.layout.Page;
import com.xenoage.zong.layout.frames.TextFrame;
import com.xenoage.zong.musicxml.types.MxlCredit;
import com.xenoage.zong.musicxml.types.MxlCreditWords;
import com.xenoage.zong.musicxml.types.MxlFormattedText;
import com.xenoage.zong.musicxml.types.MxlScorePartwise;
import com.xenoage.zong.musicxml.types.attributes.MxlPosition;
import com.xenoage.zong.musicxml.types.choice.MxlCreditContent.MxlCreditContentType;
import com.xenoage.zong.musicxml.types.enums.MxlVAlign;
/**
* This class reads the credit elements of a MusicXML 2.0
* document and creates {@link TextFrame}s out of them.
*
* @author Andreas Wenger
*/
public final class CreditsReader {
/**
* Reads the top-level credit elements of the given MusicXML 2.0
* document, creates {@link TextFrame}s out of them and adds
* them to the given {@link Layout}, using the given {@link ScoreFormat}.
*/
public static void read(MxlScorePartwise mxlScorePartwise, Layout layout, ScoreFormat scoreFormat) {
Layout ret = layout;
//get all credit elements
for (MxlCredit credit : it(mxlScorePartwise.getScoreHeader().getCredits())) {
addTextFrame(credit, ret, scoreFormat);
}
}
/**
* Adds the given credit element as a {@link TextFrame} to the given {@link Layout}.
*/
private static void addTextFrame(MxlCredit credit, Layout layout, ScoreFormat scoreFormat) {
if (credit.getContent().getCreditContentType() == MxlCreditContentType.CreditWords) {
MxlCreditWords mxlCreditWords = (MxlCreditWords) credit.getContent();
//create formatted text
FormattedText text = createFormattedText(mxlCreditWords, scoreFormat.getLyricFont());
//compute position (read only the position of the first credit-words element)
Page firstPage = layout.getPages().get(0);
float tenths = scoreFormat.getInterlineSpace() / 10;
MxlFormattedText mxlFirstCreditWords = mxlCreditWords.getItems().get(0);
MxlPosition mxlPosition = mxlFirstCreditWords.getPrintStyle().getPosition();
Point2f offsetFromBottomInTenths = mxlPosition.getDefault().or(new Point2f(10f, 10f));
Point2f position = new Point2f(offsetFromBottomInTenths.x * tenths,
firstPage.getFormat().getSize().height - offsetFromBottomInTenths.y * tenths);
//compute size
//this is the width of the widest paragraph and the height of the sum of all paragraphs
float maxParagraphWidth = 1; //at least 1 mm
float sumParagraphsHeight = 1; //at least 1 mm
for (FormattedTextParagraph paragraph : text.getParagraphs()) {
maxParagraphWidth = Math.max(maxParagraphWidth, paragraph.getMetrics().getWidth());
sumParagraphsHeight += paragraph.getHeightMm();
}
Size2f size = new Size2f(maxParagraphWidth, sumParagraphsHeight);
//horizontal alignment:
//try with halign first, and if not set, use justify
Alignment alignment = readAlignment(mxlFirstCreditWords.getHAlign());
if (alignment == null) {
alignment = readAlignment(mxlFirstCreditWords.getJustify());
}
if (alignment == null || alignment == Alignment.Left) {
position = position.add(size.width / 2, 0);
}
else if (alignment == Alignment.Center) {
//already centered
}
else if (alignment == Alignment.Right) {
position = position.add(-size.width / 2, 0);
}
//vertical alignment
MxlVAlign mxlVAlign = mxlFirstCreditWords.getVAlign();
if (mxlVAlign == MxlVAlign.Top || mxlVAlign == MxlVAlign.Unknown) {
position = position.add(0, size.height / 2);
}
else if (mxlVAlign == MxlVAlign.Middle) {
//already centered
}
else if (mxlVAlign == MxlVAlign.Bottom || mxlVAlign == MxlVAlign.Baseline) {
position = position.add(0, -size.height / 2);
}
//create and add TextFrame
TextFrame textFrame = new TextFrame();
textFrame.setPosition(position);
textFrame.setSize(size);
textFrame.setText(text);
layout.getPages().get(0).addFrame(textFrame);
}
}
/**
* Creates a {@link FormattedText} and returns it.
*/
private static FormattedText createFormattedText(MxlCreditWords mxlCreditWords,
FontInfo defaultFont) {
CList<FormattedTextParagraph> paragraphs = clist();
CList<FormattedTextElement> elements = clist();
//iterate through all credit-words elements.
//currently we are only interested in credit-words elements on page 1.
Alignment alignment = FormattedTextParagraph.defaultAlignment;
for (MxlFormattedText mxlFormattedText : mxlCreditWords.getItems()) {
//read text. if empty, ignore this element
String textContent = mxlFormattedText.getValue();
if (textContent.length() == 0)
continue;
//apply horizontal alignment, if set, otherwise keep the old value
Alignment newAlignment = readAlignment(mxlFormattedText.getJustify());
if (newAlignment != null) {
alignment = newAlignment;
}
//since we use paragraphs but MusicXML doesn't, split
//the text where there are line breaks.
String[] textLines = textContent.split("\n");
boolean endsWithLineBreak = textContent.endsWith("\n");
//append the first line to the current paragraph, then create
//new paragraphs for the following lines
for (int iLine = 0; iLine < textLines.length; iLine++) {
if (iLine > 0) {
//not the first line: we have to create a new paragraph
paragraphs.add(new FormattedTextParagraph(elements.close(), alignment));
elements = clist();
}
//read line
String textLine = textLines[iLine];
if (textLine.length() > 0) {
//font
FontInfo fontInfo = new FontInfoReader(mxlFormattedText.getPrintStyle().getFont(), defaultFont).read();
//color
Color color = mxlFormattedText.getPrintStyle().getColor().getValue();
//create text element
FormattedTextElement textElement = new FormattedTextString(textLine,
new FormattedTextStyle(fontInfo, color, null));
elements.add(textElement);
}
}
if (endsWithLineBreak) {
//create a new paragraph
paragraphs.add(new FormattedTextParagraph(elements.close(), alignment));
elements = clist();
}
}
//add non-empty paragraph at the end
if (elements.size() > 0) {
paragraphs.add(new FormattedTextParagraph(elements.close(), alignment));
}
return new FormattedText(paragraphs.close());
}
}