package com.door43.translationstudio.spannables; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.BackgroundColorSpan; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; import android.text.style.StyleSpan; import com.door43.tools.reporting.Logger; import com.door43.translationstudio.R; import com.door43.translationstudio.AppContext; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; /** * Class to create NoteSpans from USFM format text */ public class USFMNoteSpan extends NoteSpan { private final CharSequence mNotes; private final CharSequence mPassage; private final String mCaller; private static final String DEFAULT_CALLER = "+"; private String mStyle; private SpannableStringBuilder mSpannable; public static final String PATTERN = "\\\\f\\s(\\S)\\s(.+)\\\\f\\*"; public static final String CHAR_PATTERN = "\\\\f([^\\*\\s]+)\\s([^\\\\]+)(?:\\\\f\\1\\*)?"; /** * @param style the note style * @param caller the note caller * @param chars a list of char elements that make up the note */ public USFMNoteSpan(String style, String caller, List<USFMChar> chars) { super(); CharSequence spanTitle = ""; CharSequence note = ""; CharSequence quotation = ""; CharSequence altQuotation = ""; CharSequence passageText = ""; for(USFMChar c:chars) { if(c.style.equals(USFMChar.STYLE_PASSAGE_TEXT)) { passageText = c.value; } else if(c.style.equals(USFMChar.STYLE_FOOTNOTE_QUOTATION)) { quotation = c.value; } else if(c.style.equals(USFMChar.STYLE_FOOTNOTE_ALT_QUOTATION)) { altQuotation = c.value; } else { // TODO: implement better. We may need to format the values note = TextUtils.concat(note, c.value); } } // set the span title if(!TextUtils.isEmpty(passageText)) { spanTitle = passageText; } else if(!TextUtils.isEmpty(quotation)) { spanTitle = quotation; } init(spanTitle, generateTag(style, caller, spanTitle, chars)); mCaller = caller; mPassage = spanTitle; mNotes = TextUtils.concat(note, " ", altQuotation); mStyle = style; } @Override public SpannableStringBuilder render() { if(mSpannable == null) { mSpannable = super.render(); // apply custom styles if(getHumanReadable().toString().isEmpty()) { Bitmap image = BitmapFactory.decodeResource(AppContext.context().getResources(), R.drawable.ic_description_black_24dp); BitmapDrawable background = new BitmapDrawable(AppContext.context().getResources(), image); background.setBounds(0, 0, background.getMinimumWidth(), background.getMinimumHeight()); mSpannable.setSpan(new ImageSpan(background), 0, mSpannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } else { mSpannable.setSpan(new BackgroundColorSpan(AppContext.context().getResources().getColor(R.color.footnote_yellow)), 0, mSpannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); mSpannable.setSpan(new StyleSpan(Typeface.ITALIC), 0, mSpannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSpannable.setSpan(new ForegroundColorSpan(AppContext.context().getResources().getColor(R.color.dark_gray)), 0, mSpannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } return mSpannable; } /** * Generates the passage note tag with additional attributes * @param style * @param caller * @param title * @param chars * @return */ public static CharSequence generateTag(String style, String caller, CharSequence title, List<USFMChar> chars) { String tag = "\\f " + caller + " "; for(USFMChar c: chars) { switch(c.style) { case USFMChar.STYLE_FOOTNOTE_VERSE: tag += "\\fv " + c.value + "\\fv*"; break; default: tag += "\\" + c.style + " " + c.value + " "; break; } } tag += "\\f*"; return tag; } /** * Generates a custom Doku Wiki footnote tag. * TODO: I think this will just be used for footnotes, however if footnotes are to be treated normally we won't have the span text. * @return */ public String generateDokuWikiTag() { return "((ref:\""+ mPassage +"\",note:\""+ mNotes +"\"))"; } /** * returns the caller * @return */ public String getCaller() { return mCaller; } /** * Returns the type of note this is * @return */ public String getStyle() { return mStyle; } /** * Returns the notes regarding the passage * @return */ public CharSequence getNotes() { return mNotes; } /** * Returns the text upon which the notes are made * @return */ public CharSequence getPassage() { return mPassage; } /** * Generates a footnote span * @param note the note * @return */ public static USFMNoteSpan generateFootnote(CharSequence note) { List<USFMChar> chars = new ArrayList<>(); chars.add(new USFMChar(USFMChar.STYLE_FOOTNOTE_TEXT, note)); return new USFMNoteSpan("f", DEFAULT_CALLER, chars); } /** * Generates a new note span from the enclosed text and returns it. * Don't forget to set the click listener! * we are using usfm for footnotes and our own variant for user notes * http://ubs-icap.org/chm/usfm/2.4/index.html * @param caller * @param noteText * @return */ public static USFMNoteSpan parseNote(CharSequence caller, CharSequence noteText) { List<USFMChar> chars = new ArrayList<>(); Pattern pattern = Pattern.compile(CHAR_PATTERN); Matcher matcher = pattern.matcher(noteText); int lastIndex = 0; CharSequence note = ""; while(matcher.find()) { int start = matcher.start(); if (start > lastIndex) { note = TextUtils.concat(note, noteText.subSequence(lastIndex, start)); } chars.add (new USFMChar("f" + matcher.group(1),matcher.group(2))); lastIndex = matcher.end(); } if(lastIndex < noteText.length()) { // if extra text, add it note = TextUtils.concat(note, noteText.subSequence(lastIndex, noteText.length())); chars.add (new USFMChar(USFMChar.STYLE_PASSAGE_TEXT,note)); } return new USFMNoteSpan("f", caller.toString(), chars); } }