package net.nightwhistler.htmlspanner.style;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.*;
import android.util.Log;
import net.nightwhistler.htmlspanner.FontFamily;
import net.nightwhistler.htmlspanner.HtmlSpanner;
import net.nightwhistler.htmlspanner.SpanCallback;
import net.nightwhistler.htmlspanner.spans.*;
import static java.lang.Math.min;
/**
* Created with IntelliJ IDEA.
* User: alex
* Date: 5/6/13
* Time: 3:18 PM
* To change this template use File | Settings | File Templates.
*/
public class StyleCallback implements SpanCallback {
private int start;
private int end;
private FontFamily defaultFont;
private Style useStyle;
public StyleCallback( FontFamily defaultFont, Style style, int start, int end ) {
this.defaultFont = defaultFont;
this.useStyle = style;
this.start = start;
this.end = end;
}
@Override
public void applySpan(HtmlSpanner spanner, SpannableStringBuilder builder) {
if ( useStyle.getFontFamily() != null || useStyle.getFontStyle() != null || useStyle.getFontWeight() != null ) {
FontFamilySpan originalSpan = getFontFamilySpan(builder, start, end);
FontFamilySpan newSpan;
if ( useStyle.getFontFamily() == null && originalSpan == null ) {
newSpan = new FontFamilySpan(this.defaultFont);
} else if ( useStyle.getFontFamily() != null ) {
newSpan = new FontFamilySpan( useStyle.getFontFamily() );
} else {
newSpan = new FontFamilySpan(originalSpan.getFontFamily());
}
if ( useStyle.getFontWeight() != null ) {
newSpan.setBold( useStyle.getFontWeight() == Style.FontWeight.BOLD );
} else if ( originalSpan != null ) {
newSpan.setBold( originalSpan.isBold() );
}
if ( useStyle.getFontStyle() != null ) {
newSpan.setItalic( useStyle.getFontStyle() == Style.FontStyle.ITALIC );
} else if ( originalSpan != null ) {
newSpan.setItalic( originalSpan.isItalic() );
}
//Log.d("StyleCallback", "Applying FontFamilySpan from " + start + " to " + end + " on text " + builder.subSequence(start, end));
//Log.d("StyleCallback", "FontFamilySpan: " + newSpan );
builder.setSpan(newSpan, start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
//If there's no border, we use a BackgroundColorSpan to draw colour behind the text
if ( spanner.isUseColoursFromStyle() && useStyle.getBackgroundColor() != null && useStyle.getBorderStyle() == null ) {
//Log.d("StyleCallback", "Applying BackgroundColorSpan with color " + useStyle.getBackgroundColor() + " from " + start + " to " + end + " on text " + builder.subSequence(start, end));
builder.setSpan(new BackgroundColorSpan(useStyle.getBackgroundColor()), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
//If there is a border, the BorderSpan will also draw the background colour if needed.
if ( useStyle.getBorderStyle() != null ) {
builder.setSpan(new BorderSpan(useStyle, start, end, spanner.isUseColoursFromStyle()), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ( useStyle.getFontSize() != null ) {
StyleValue styleValue = useStyle.getFontSize();
if ( styleValue.getUnit() == StyleValue.Unit.PX ) {
if ( styleValue.getIntValue() > 0 ) {
// Log.d("StyleCallback", "Applying AbsoluteSizeSpan with size " + useStyle.getAbsoluteFontSize() + " from " + start + " to " + end + " on text " + builder.subSequence(start, end));
builder.setSpan(new AbsoluteSizeSpan(styleValue.getIntValue()), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
if ( styleValue.getFloatValue() > 0f ) {
//Log.d("StyleCallback", "Applying RelativeSizeSpan with size " + useStyle.getRelativeFontSize() + " from " + start + " to " + end + " on text " + builder.subSequence(start, end));
builder.setSpan(new RelativeSizeSpan(styleValue.getFloatValue()), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
if ( spanner.isUseColoursFromStyle() && useStyle.getColor() != null ) {
//Log.d("StyleCallback", "Applying ForegroundColorSpan from " + start + " to " + end + " on text " + builder.subSequence(start, end) );
builder.setSpan(new ForegroundColorSpan(useStyle.getColor()), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ( useStyle.getTextAlignment() != null ) {
AlignmentSpan alignSpan = null;
switch ( useStyle.getTextAlignment() ) {
case LEFT:
alignSpan = new AlignNormalSpan();
break;
case CENTER:
alignSpan = new CenterSpan();
break;
case RIGHT:
alignSpan = new AlignOppositeSpan();
break;
}
//Log.d("StyleCallback", "Applying AlignmentSpan from " + start + " to " + end + " on text " + builder.subSequence(start, end) );
builder.setSpan(alignSpan, start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ( useStyle.getTextIndent() != null ) {
StyleValue styleValue = useStyle.getTextIndent();
int marginStart = start;
while ( marginStart < end && builder.charAt(marginStart) == '\n' ) {
marginStart++;
}
int marginEnd = min( end, marginStart +1 );
Log.d("StyleCallback", "Applying LeadingMarginSpan from " + marginStart + " to " + marginEnd +
" on text " + builder.subSequence(marginStart, marginEnd));
if ( styleValue.getUnit() == StyleValue.Unit.PX ) {
if ( styleValue.getIntValue() > 0 ) {
builder.setSpan(new LeadingMarginSpan.Standard(styleValue.getIntValue(), 0), marginStart, marginEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
if ( styleValue.getFloatValue() > 0f ) {
builder.setSpan(new LeadingMarginSpan.Standard( (int)
( HtmlSpanner.HORIZONTAL_EM_WIDTH * styleValue.getFloatValue()), 0), marginStart, marginEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
/* We ignore negative horizontal margins, since that would cause the text to be rendered off-screen */
if ( useStyle.getMarginLeft() != null ) {
StyleValue styleValue = useStyle.getMarginLeft();
if ( styleValue.getUnit() == StyleValue.Unit.PX ) {
if ( styleValue.getIntValue() > 0 ) {
builder.setSpan(new LeadingMarginSpan.Standard(styleValue.getIntValue() ), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else if ( styleValue.getFloatValue() > 0f ) {
builder.setSpan(new LeadingMarginSpan.Standard(
(int) (HtmlSpanner.HORIZONTAL_EM_WIDTH * styleValue.getFloatValue())), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
/**
* Returns the current FontFamilySpan in use on the given subsection of the builder.
*
* If no FontFamily has been set yet, spanner.getDefaultFont() is returned.
*
* @param builder the text to check
* @param start start of the section
* @param end end of the section
* @return a FontFamily object
*/
private FontFamilySpan getFontFamilySpan( SpannableStringBuilder builder, int start, int end ) {
FontFamilySpan[] spans = builder.getSpans(start, end, FontFamilySpan.class);
if ( spans != null && spans.length > 0 ) {
return spans[spans.length-1];
}
return null;
}
}