diff options
Diffstat (limited to 'libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/HtmlStyleUtils.java')
-rw-r--r-- | libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/HtmlStyleUtils.java | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/HtmlStyleUtils.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/HtmlStyleUtils.java new file mode 100644 index 000000000..912781f1f --- /dev/null +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/HtmlStyleUtils.java @@ -0,0 +1,150 @@ +package org.wordpress.android.editor; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Build; +import android.support.annotation.NonNull; +import android.text.Spannable; +import android.text.style.CharacterStyle; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; + +import org.wordpress.android.util.AppLog; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class HtmlStyleUtils { + public static final int TAG_COLOR = Color.rgb(0, 80, 130); + public static final int ATTRIBUTE_COLOR = Color.rgb(158, 158, 158); + + public static final String REGEX_HTML_TAGS = "(<\\/?[a-z][^<>]*>)"; + public static final String REGEX_HTML_ATTRIBUTES = "(?<==)('|\")(.*?\\1)(?=.*?>)"; + public static final String REGEX_HTML_COMMENTS = "(<!--.*?-->)"; + public static final String REGEX_HTML_ENTITIES = "("|&|'|<|>| |¡|¢|£" + + "|¤|¥|¦|§|¨|©|ª|«|¬|­|®|¯|°|±" + + "|²|³|´|µ|¶|·|¸|¹|º|»|¼|½|¾|¿" + + "|À|Á|Â|Ã|Ä|Å|Æ|Ç|È|É|Ê|Ë|Ì|Í" + + "|Î|Ï|Ð|Ñ|Ò|Ó|Ô|Õ|Ö|×|Ø|Ù|Ú|Û" + + "|Ü|Ý|Þ|ß|à|á|â|ã|ä|å|æ|ç|è|é" + + "|ê|ë|ì|í|î|ï|ð|ñ|ò|ó|ô|õ|ö|÷" + + "|ø|ù|ú|û|ü|ý|þ|ÿ|Œ|œ|Š|š|Ÿ|ƒ" + + "|ˆ|˜|Α|Β|Γ|Δ|Ε|Ζ|Η|Θ|Ι|Κ|Λ|Μ" + + "|Ν|Ξ|Ο|Π|Ρ|Σ|Τ|Υ|Φ|Χ|Ψ|Ω|α|β" + + "|γ|δ|ε|ζ|η|θ|ι|κ|λ|μ|ν|ξ|ο|π" + + "|ρ|ς|σ|τ|υ|φ|χ|ψ|ω|ϑ|ϒ|ϖ| | " + + "| |‌|‍|‎|‏|–|—|‘|’|‚|“|”|„" + + "|†|‡|•|…|‰|′|″|‹|›|‾|⁄|€|ℑ" + + "|℘|ℜ|™|ℵ|←|↑|→|↓|↔|↵|⇐|⇑|⇒" + + "|⇓|⇔|∀|∂|∃|∅|∇|∈|∉|∋|∏|∑|−" + + "|∗|√|∝|∞|∠|∧|∨|∩|∪|∫|∴|∼|≅" + + "|≈|≠|≡|≤|≥|⊂|⊃|⊄|⊆|⊇|⊕|⊗|⊥" + + "|⋅|⌈|⌉|⌊|⌋|〈|〉|◊|♠|♣|♥|♦|"" + + "|&|'|<|>| |¡|¢|£|¤|¥|¦|§|¨|©|ª" + + "|«|¬|­|®|¯|°|±|²|³|´|µ|¶|·|¸" + + "|¹|º|»|¼|½|¾|¿|À|Á|Â|Ã|Ä" + + "|Å|Æ|Ç|È|É|Ê|Ë|Ì|Í|Î|Ï|Ð" + + "|Ñ|Ò|Ó|Ô|Õ|Ö|×|Ø|Ù|Ú|Û|Ü" + + "|Ý|Þ|ß|à|á|â|ã|ä|å|æ|ç|è" + + "|é|ê|ë|ì|í|î|ï|ð|ñ|ò|ó|ô" + + "|õ|ö|÷|ø|Ù|Ú|Û|Ü|ý|þ|ÿ|Œ" + + "|œ|Š|š|Ÿ|ƒ|ˆ|˜|Α|Β|Γ|Δ|Ε|Ζ" + + "|Η|Θ|Ι|Κ|Λ|Μ|Ν|Ξ|Ο|Π|Ρ|Σ|Τ|Υ|Φ" + + "|Χ|Ψ|Ω|α|β|γ|δ|ε|ζ|η|θ|ι|κ" + + "|λ|μ|ν|ξ|ο|π|ρ|ς|σ|τ|υ|φ|χ|ψ|ω" + + "|ϑ|&Upsih;|ϖ| | | |‌|‍|‎|‏|–|—|‘" + + "|’|‚|“|”|„|†|‡|•|…|‰|′|″" + + "|‹|›|‾|⁄|€|ℑ|℘|ℜ|™|ℵ|←|↑|→" + + "|↓|↔|↵|⇐|&UArr;|⇒|⇓|⇔|∀|∂|∃|∅|∇|∈" + + "|∉|∋|∏|∑|−|∗|√|∝|∞|∠|∧|∨|∩|∪|∫" + + "|∴|∼|≅|≈|≠|≡|≤|≥|⊂|⊃|⊄|⊆|⊇|⊕|⊗" + + "|⊥|⋅|⌈|⌉|⌊|⌋|⟨|⟩|◊|♠|♣|♥|♦)"; + + public static final int SPANNABLE_FLAGS = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE; + + /** + * Apply styling rules to {@code content}. + */ + public static void styleHtmlForDisplay(@NonNull Spannable content) { + styleHtmlForDisplay(content, 0, content.length()); + } + + /** + * Apply styling rules to {@code content} inside the range from {@code start} to {@code end}. + * + * @param content the Spannable to apply style rules to + * @param start the index in {@code content} to start styling from + * @param end the index in {@code content} to style until + */ + public static void styleHtmlForDisplay(@NonNull Spannable content, int start, int end) { + if (Build.VERSION.RELEASE.equals("4.1") || Build.VERSION.RELEASE.equals("4.1.1")) { + // Avoids crashing bug in Android 4.1 and 4.1.1 triggered when spanned text is line-wrapped + // AOSP issue: https://code.google.com/p/android/issues/detail?id=35466 + return; + } + + applySpansByRegex(content, start, end, REGEX_HTML_TAGS); + applySpansByRegex(content, start, end, REGEX_HTML_ATTRIBUTES); + applySpansByRegex(content, start, end, REGEX_HTML_COMMENTS); + applySpansByRegex(content, start, end, REGEX_HTML_ENTITIES); + } + + /** + * Applies styles to {@code content} from {@code start} to {@code end}, based on rule {@code regex}. + * @param content the Spannable to apply style rules to + * @param start the index in {@code content} to start styling from + * @param end the index in {@code content} to style until + * @param regex the pattern to match for styling + */ + private static void applySpansByRegex(Spannable content, int start, int end, String regex) { + if (content == null || start < 0 || end < 0 || start > content.length() || end > content.length() || + start >= end) { + AppLog.d(AppLog.T.EDITOR, "applySpansByRegex() received invalid input"); + return; + } + + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(content.subSequence(start, end)); + + while (matcher.find()) { + int matchStart = matcher.start() + start; + int matchEnd = matcher.end() + start; + switch(regex) { + case REGEX_HTML_TAGS: + content.setSpan(new ForegroundColorSpan(TAG_COLOR), matchStart, matchEnd, SPANNABLE_FLAGS); + break; + case REGEX_HTML_ATTRIBUTES: + content.setSpan(new ForegroundColorSpan(ATTRIBUTE_COLOR), matchStart, matchEnd, SPANNABLE_FLAGS); + break; + case REGEX_HTML_COMMENTS: + content.setSpan(new ForegroundColorSpan(ATTRIBUTE_COLOR), matchStart, matchEnd, SPANNABLE_FLAGS); + content.setSpan(new StyleSpan(Typeface.ITALIC), matchStart, matchEnd, SPANNABLE_FLAGS); + content.setSpan(new RelativeSizeSpan(0.75f), matchStart, matchEnd, SPANNABLE_FLAGS); + break; + case REGEX_HTML_ENTITIES: + content.setSpan(new ForegroundColorSpan(TAG_COLOR), matchStart, matchEnd, SPANNABLE_FLAGS); + content.setSpan(new StyleSpan(Typeface.BOLD), matchStart, matchEnd, SPANNABLE_FLAGS); + content.setSpan(new RelativeSizeSpan(0.75f), matchStart, matchEnd, SPANNABLE_FLAGS); + break; + } + } + } + + /** + * Clears all relevant spans in {@code content} from {@code start} to {@code end}. Relevant spans are the subclasses + * of {@link CharacterStyle} applied by {@link HtmlStyleUtils#applySpansByRegex(Spannable, int, int, String)}. + * @param content the Spannable to clear styles from + * @param spanStart the index in {@code content} to start clearing styles from + * @param spanEnd the index in {@code content} to clear styles until + */ + public static void clearSpans(Spannable content, int spanStart, int spanEnd) { + CharacterStyle[] spans = content.getSpans(spanStart, spanEnd, CharacterStyle.class); + + for (CharacterStyle span : spans) { + if (span instanceof ForegroundColorSpan || span instanceof StyleSpan || span instanceof RelativeSizeSpan) { + content.removeSpan(span); + } + } + } +} |