mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
WebVTT styling.
- parse webvtt cue - remove all tags from string (supported or not) - apply spans for b, i and u - honor class names in tags to properly parse the cue but do not apply styles for them
This commit is contained in:
parent
cdb6ac4073
commit
1a9b2be551
@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.text.webvtt;
|
||||||
|
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.test.InstrumentationTestCase;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.style.StyleSpan;
|
||||||
|
import android.text.style.UnderlineSpan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for {@link WebvttCueParser}.
|
||||||
|
*/
|
||||||
|
public final class WebvttCueParserTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
|
public void testParseStrictValidClassesAndTrailingTokens() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("<v.first.loud Esme>"
|
||||||
|
+ "This <u.style1.style2 some stuff>is</u> text with <b.foo><i.bar>html</i></b> tags");
|
||||||
|
|
||||||
|
assertEquals("This is text with html tags", text.toString());
|
||||||
|
|
||||||
|
UnderlineSpan[] underlineSpans = getSpans(text, UnderlineSpan.class);
|
||||||
|
StyleSpan[] styleSpans = getSpans(text, StyleSpan.class);
|
||||||
|
assertEquals(1, underlineSpans.length);
|
||||||
|
assertEquals(2, styleSpans.length);
|
||||||
|
assertEquals(Typeface.ITALIC, styleSpans[0].getStyle());
|
||||||
|
assertEquals(Typeface.BOLD, styleSpans[1].getStyle());
|
||||||
|
|
||||||
|
assertEquals(5, text.getSpanStart(underlineSpans[0]));
|
||||||
|
assertEquals(7, text.getSpanEnd(underlineSpans[0]));
|
||||||
|
assertEquals(18, text.getSpanStart(styleSpans[0]));
|
||||||
|
assertEquals(18, text.getSpanStart(styleSpans[1]));
|
||||||
|
assertEquals(22, text.getSpanEnd(styleSpans[0]));
|
||||||
|
assertEquals(22, text.getSpanEnd(styleSpans[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseStrictValidUnsupportedTagsStrippedOut() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse(
|
||||||
|
"<v.first.loud Esme>This <unsupported>is</unsupported> text with "
|
||||||
|
+ "<notsupp><invalid>html</invalid></notsupp> tags");
|
||||||
|
|
||||||
|
assertEquals("This is text with html tags", text.toString());
|
||||||
|
assertEquals(0, getSpans(text, UnderlineSpan.class).length);
|
||||||
|
assertEquals(0, getSpans(text, StyleSpan.class).length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseWellFormedUnclosedEndAtCueEnd() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse(
|
||||||
|
"An <u some trailing stuff>unclosed u tag with <i>italic</i> inside");
|
||||||
|
|
||||||
|
assertEquals("An unclosed u tag with italic inside", text.toString());
|
||||||
|
|
||||||
|
UnderlineSpan[] underlineSpans = getSpans(text, UnderlineSpan.class);
|
||||||
|
StyleSpan[] styleSpans = getSpans(text, StyleSpan.class);
|
||||||
|
assertEquals(1, underlineSpans.length);
|
||||||
|
assertEquals(1, styleSpans.length);
|
||||||
|
assertEquals(Typeface.ITALIC, styleSpans[0].getStyle());
|
||||||
|
|
||||||
|
assertEquals(3, text.getSpanStart(underlineSpans[0]));
|
||||||
|
assertEquals(23, text.getSpanStart(styleSpans[0]));
|
||||||
|
assertEquals(29, text.getSpanEnd(styleSpans[0]));
|
||||||
|
assertEquals(36, text.getSpanEnd(underlineSpans[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseWellFormedUnclosedEndAtParent() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse(
|
||||||
|
"An unclosed u tag with <i><u>underline and italic</i> inside");
|
||||||
|
|
||||||
|
assertEquals("An unclosed u tag with underline and italic inside", text.toString());
|
||||||
|
|
||||||
|
UnderlineSpan[] underlineSpans = getSpans(text, UnderlineSpan.class);
|
||||||
|
StyleSpan[] styleSpans = getSpans(text, StyleSpan.class);
|
||||||
|
assertEquals(1, underlineSpans.length);
|
||||||
|
assertEquals(1, styleSpans.length);
|
||||||
|
|
||||||
|
assertEquals(23, text.getSpanStart(underlineSpans[0]));
|
||||||
|
assertEquals(23, text.getSpanStart(styleSpans[0]));
|
||||||
|
assertEquals(43, text.getSpanEnd(underlineSpans[0]));
|
||||||
|
assertEquals(43, text.getSpanEnd(styleSpans[0]));
|
||||||
|
|
||||||
|
assertEquals(Typeface.ITALIC, styleSpans[0].getStyle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseMalformedNestedElements() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse(
|
||||||
|
"<b><u>An unclosed u tag with <i>italic</u> inside</i></b>");
|
||||||
|
assertEquals("An unclosed u tag with italic inside", text.toString());
|
||||||
|
|
||||||
|
UnderlineSpan[] underlineSpans = getSpans(text, UnderlineSpan.class);
|
||||||
|
StyleSpan[] styleSpans = getSpans(text, StyleSpan.class);
|
||||||
|
assertEquals(1, underlineSpans.length);
|
||||||
|
assertEquals(2, styleSpans.length);
|
||||||
|
|
||||||
|
// all tags applied until matching start tag found
|
||||||
|
assertEquals(0, text.getSpanStart(underlineSpans[0]));
|
||||||
|
assertEquals(29, text.getSpanEnd(underlineSpans[0]));
|
||||||
|
if (styleSpans[0].getStyle() == Typeface.BOLD) {
|
||||||
|
assertEquals(0, text.getSpanStart(styleSpans[0]));
|
||||||
|
assertEquals(23, text.getSpanStart(styleSpans[1]));
|
||||||
|
assertEquals(29, text.getSpanEnd(styleSpans[1]));
|
||||||
|
assertEquals(36, text.getSpanEnd(styleSpans[0]));
|
||||||
|
} else {
|
||||||
|
assertEquals(0, text.getSpanStart(styleSpans[1]));
|
||||||
|
assertEquals(23, text.getSpanStart(styleSpans[0]));
|
||||||
|
assertEquals(29, text.getSpanEnd(styleSpans[0]));
|
||||||
|
assertEquals(36, text.getSpanEnd(styleSpans[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseCloseNonExistingTag() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("blah<b>blah</i>blah</b>blah");
|
||||||
|
assertEquals("blahblahblahblah", text.toString());
|
||||||
|
|
||||||
|
StyleSpan[] spans = getSpans(text, StyleSpan.class);
|
||||||
|
assertEquals(1, spans.length);
|
||||||
|
assertEquals(Typeface.BOLD, spans[0].getStyle());
|
||||||
|
assertEquals(4, text.getSpanStart(spans[0]));
|
||||||
|
assertEquals(8, text.getSpanEnd(spans[0])); // should be 12 when valid
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseEmptyTagName() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("An unclosed u tag with <>italic inside");
|
||||||
|
assertEquals("An unclosed u tag with italic inside", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseEntities() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("& > < ");
|
||||||
|
assertEquals("& > < ", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseEntitiesUnsupported() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("&noway; &sure;");
|
||||||
|
assertEquals(" ", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseEntitiesNotTerminated() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("& here comes text");
|
||||||
|
assertEquals("& here comes text", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseEntitiesNotTerminatedUnsupported() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("&surenot here comes text");
|
||||||
|
assertEquals(" here comes text", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseEntitiesNotTerminatedNoSpace() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("&surenot");
|
||||||
|
assertEquals("&surenot", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseVoidTag() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("here comes<br/> text<br/>");
|
||||||
|
assertEquals("here comes text", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseMultipleTagsOfSameKind() {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("blah <b>blah</b> blah <b>foo</b>");
|
||||||
|
|
||||||
|
assertEquals("blah blah blah foo", text.toString());
|
||||||
|
StyleSpan[] spans = getSpans(text, StyleSpan.class);
|
||||||
|
assertEquals(2, spans.length);
|
||||||
|
assertEquals(5, text.getSpanStart(spans[0]));
|
||||||
|
assertEquals(9, text.getSpanEnd(spans[0]));
|
||||||
|
assertEquals(15, text.getSpanStart(spans[1]));
|
||||||
|
assertEquals(18, text.getSpanEnd(spans[1]));
|
||||||
|
assertEquals(Typeface.BOLD, spans[0].getStyle());
|
||||||
|
assertEquals(Typeface.BOLD, spans[1].getStyle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseInvalidVoidSlash() {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse("blah <b/.st1.st2 trailing stuff> blah");
|
||||||
|
|
||||||
|
assertEquals("blah blah", text.toString());
|
||||||
|
StyleSpan[] spans = getSpans(text, StyleSpan.class);
|
||||||
|
assertEquals(0, spans.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseMonkey() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse(
|
||||||
|
"< u>An unclosed u tag with <<<<< i>italic</u></u></u></u ></i><u><u> inside");
|
||||||
|
assertEquals("An unclosed u tag with italic inside", text.toString());
|
||||||
|
text = parser.parse(">>>>>>>>>An unclosed u tag with <<<<< italic</u></u></u></u >"
|
||||||
|
+ "</i><u><u> inside");
|
||||||
|
assertEquals(">>>>>>>>>An unclosed u tag with inside", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseCornerCases() throws Exception {
|
||||||
|
WebvttCueParser parser = new WebvttCueParser();
|
||||||
|
Spanned text = parser.parse(">");
|
||||||
|
assertEquals(">", text.toString());
|
||||||
|
|
||||||
|
text = parser.parse("<");
|
||||||
|
assertEquals("", text.toString());
|
||||||
|
|
||||||
|
text = parser.parse("<b.st1.st2 annotation");
|
||||||
|
assertEquals("", text.toString());
|
||||||
|
|
||||||
|
text = parser.parse("<<<<<<<<<<<<<<<<");
|
||||||
|
assertEquals("", text.toString());
|
||||||
|
|
||||||
|
text = parser.parse("<<<<<<>><<<<<<<<<<");
|
||||||
|
assertEquals(">", text.toString());
|
||||||
|
|
||||||
|
text = parser.parse("<>");
|
||||||
|
assertEquals("", text.toString());
|
||||||
|
|
||||||
|
text = parser.parse("&");
|
||||||
|
assertEquals("&", text.toString());
|
||||||
|
|
||||||
|
text = parser.parse("&&&&&&&");
|
||||||
|
assertEquals("&&&&&&&", text.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T[] getSpans(Spanned text, Class<T> spanType) {
|
||||||
|
return text.getSpans(0, text.length(), spanType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.text.webvtt;
|
||||||
|
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.style.StyleSpan;
|
||||||
|
import android.text.style.UnderlineSpan;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser for webvtt cue text. (https://w3c.github.io/webvtt/#cue-text)
|
||||||
|
*/
|
||||||
|
/* package */ final class WebvttCueParser {
|
||||||
|
|
||||||
|
private static final char CHAR_LESS_THAN = '<';
|
||||||
|
private static final char CHAR_GREATER_THAN = '>';
|
||||||
|
private static final char CHAR_SLASH = '/';
|
||||||
|
private static final char CHAR_AMPERSAND = '&';
|
||||||
|
private static final char CHAR_SEMI_COLON = ';';
|
||||||
|
private static final char CHAR_SPACE = ' ';
|
||||||
|
private static final String SPACE = " ";
|
||||||
|
|
||||||
|
private static final String ENTITY_LESS_THAN = "lt";
|
||||||
|
private static final String ENTITY_GREATER_THAN = "gt";
|
||||||
|
private static final String ENTITY_AMPERSAND = "amp";
|
||||||
|
private static final String ENTITY_NON_BREAK_SPACE = "nbsp";
|
||||||
|
|
||||||
|
private static final String TAG_BOLD = "b";
|
||||||
|
private static final String TAG_ITALIC = "i";
|
||||||
|
private static final String TAG_UNDERLINE = "u";
|
||||||
|
private static final String TAG_CLASS = "c";
|
||||||
|
private static final String TAG_VOICE = "v";
|
||||||
|
private static final String TAG_LANG = "lang";
|
||||||
|
|
||||||
|
private static final int STYLE_BOLD = Typeface.BOLD;
|
||||||
|
private static final int STYLE_ITALIC = Typeface.ITALIC;
|
||||||
|
|
||||||
|
private static final String TAG = "WebvttCueParser";
|
||||||
|
|
||||||
|
public Spanned parse(String markup) {
|
||||||
|
SpannableStringBuilder spannedText = new SpannableStringBuilder();
|
||||||
|
Stack<StartTag> startTagStack = new Stack<>();
|
||||||
|
String[] tagTokens;
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < markup.length()) {
|
||||||
|
char curr = markup.charAt(pos);
|
||||||
|
switch (curr) {
|
||||||
|
case CHAR_LESS_THAN:
|
||||||
|
if (pos + 1 >= markup.length()) {
|
||||||
|
pos++;
|
||||||
|
break; // avoid ArrayOutOfBoundsException
|
||||||
|
}
|
||||||
|
int ltPos = pos;
|
||||||
|
boolean isClosingTag = markup.charAt(ltPos + 1) == CHAR_SLASH;
|
||||||
|
pos = findEndOfTag(markup, ltPos + 1);
|
||||||
|
boolean isVoidTag = markup.charAt(pos - 2) == CHAR_SLASH;
|
||||||
|
|
||||||
|
tagTokens = tokenizeTag(markup.substring(
|
||||||
|
ltPos + (isClosingTag ? 2 : 1), isVoidTag ? pos - 2 : pos - 1));
|
||||||
|
if (tagTokens == null || !isSupportedTag(tagTokens[0])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isClosingTag) {
|
||||||
|
StartTag startTag;
|
||||||
|
do {
|
||||||
|
if (startTagStack.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
startTag = startTagStack.pop();
|
||||||
|
applySpansForTag(startTag, spannedText);
|
||||||
|
} while(!startTag.name.equals(tagTokens[0]));
|
||||||
|
} else if (!isVoidTag) {
|
||||||
|
startTagStack.push(new StartTag(tagTokens[0], spannedText.length()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHAR_AMPERSAND:
|
||||||
|
int semiColonEnd = markup.indexOf(CHAR_SEMI_COLON, pos + 1);
|
||||||
|
int spaceEnd = markup.indexOf(CHAR_SPACE, pos + 1);
|
||||||
|
int entityEnd = semiColonEnd == -1 ? spaceEnd
|
||||||
|
: spaceEnd == -1 ? semiColonEnd : Math.min(semiColonEnd, spaceEnd);
|
||||||
|
if (entityEnd != -1) {
|
||||||
|
applyEntity(markup.substring(pos + 1, entityEnd), spannedText);
|
||||||
|
if (entityEnd == spaceEnd) {
|
||||||
|
spannedText.append(" ");
|
||||||
|
}
|
||||||
|
pos = entityEnd + 1;
|
||||||
|
} else {
|
||||||
|
spannedText.append(curr);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
spannedText.append(curr);
|
||||||
|
pos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// apply unclosed tags
|
||||||
|
while (!startTagStack.isEmpty()) {
|
||||||
|
applySpansForTag(startTagStack.pop(), spannedText);
|
||||||
|
}
|
||||||
|
return spannedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find end of tag (>). The position returned is the position of the > plus one (exclusive).
|
||||||
|
*
|
||||||
|
* @param markup The webvtt cue markup to be parsed.
|
||||||
|
* @param startPos the position from where to start searching for the end of tag.
|
||||||
|
* @return the position of the end of tag plus 1 (one).
|
||||||
|
*/
|
||||||
|
private int findEndOfTag(String markup, int startPos) {
|
||||||
|
int idx = markup.indexOf(CHAR_GREATER_THAN, startPos);
|
||||||
|
return idx == -1 ? markup.length() : idx + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyEntity(String entity, SpannableStringBuilder spannedText) {
|
||||||
|
switch (entity) {
|
||||||
|
case ENTITY_LESS_THAN:
|
||||||
|
spannedText.append('<');
|
||||||
|
break;
|
||||||
|
case ENTITY_GREATER_THAN:
|
||||||
|
spannedText.append('>');
|
||||||
|
break;
|
||||||
|
case ENTITY_NON_BREAK_SPACE:
|
||||||
|
spannedText.append(' ');
|
||||||
|
break;
|
||||||
|
case ENTITY_AMPERSAND:
|
||||||
|
spannedText.append('&');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "ignoring unsupported entity: '&" + entity + ";'");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSupportedTag(String tagName) {
|
||||||
|
switch (tagName) {
|
||||||
|
case TAG_BOLD:
|
||||||
|
case TAG_CLASS:
|
||||||
|
case TAG_ITALIC:
|
||||||
|
case TAG_LANG:
|
||||||
|
case TAG_UNDERLINE:
|
||||||
|
case TAG_VOICE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySpansForTag(StartTag startTag, SpannableStringBuilder spannedText) {
|
||||||
|
switch(startTag.name) {
|
||||||
|
case TAG_BOLD:
|
||||||
|
spannedText.setSpan(new StyleSpan(STYLE_BOLD), startTag.position,
|
||||||
|
spannedText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
return;
|
||||||
|
case TAG_ITALIC:
|
||||||
|
spannedText.setSpan(new StyleSpan(STYLE_ITALIC), startTag.position,
|
||||||
|
spannedText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
return;
|
||||||
|
case TAG_UNDERLINE:
|
||||||
|
spannedText.setSpan(new UnderlineSpan(), startTag.position,
|
||||||
|
spannedText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tokenizes a tag expression into tag name (pos 0) and classes (pos 1..n).
|
||||||
|
*
|
||||||
|
* @param fullTagExpression characters between &lt: and &gt; of a start or end tag
|
||||||
|
* @return an array of <code>String</code>s with the tag name at pos 0 followed by style classes
|
||||||
|
* or null if it's an empty tag: '<>'
|
||||||
|
*/
|
||||||
|
private String[] tokenizeTag(String fullTagExpression) {
|
||||||
|
fullTagExpression = fullTagExpression.replace("\\s+", " ").trim();
|
||||||
|
if (fullTagExpression.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (fullTagExpression.contains(SPACE)) {
|
||||||
|
fullTagExpression = fullTagExpression.substring(0, fullTagExpression.indexOf(SPACE));
|
||||||
|
}
|
||||||
|
return fullTagExpression.split("\\.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class StartTag {
|
||||||
|
|
||||||
|
public final String name;
|
||||||
|
public final int position;
|
||||||
|
|
||||||
|
public StartTag(String name, int position) {
|
||||||
|
this.position = position;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -21,7 +21,6 @@ import com.google.android.exoplayer.text.Cue;
|
|||||||
import com.google.android.exoplayer.text.SubtitleParser;
|
import com.google.android.exoplayer.text.SubtitleParser;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.text.Html;
|
|
||||||
import android.text.Layout.Alignment;
|
import android.text.Layout.Alignment;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -48,10 +47,12 @@ public final class WebvttParser implements SubtitleParser {
|
|||||||
private static final Pattern CUE_HEADER = Pattern.compile("^(\\S+)\\s+-->\\s+(\\S+)(.*)?$");
|
private static final Pattern CUE_HEADER = Pattern.compile("^(\\S+)\\s+-->\\s+(\\S+)(.*)?$");
|
||||||
private static final Pattern CUE_SETTING = Pattern.compile("(\\S+?):(\\S+)");
|
private static final Pattern CUE_SETTING = Pattern.compile("(\\S+?):(\\S+)");
|
||||||
|
|
||||||
|
private final WebvttCueParser cueParser;
|
||||||
private final PositionHolder positionHolder;
|
private final PositionHolder positionHolder;
|
||||||
private final StringBuilder textBuilder;
|
private final StringBuilder textBuilder;
|
||||||
|
|
||||||
public WebvttParser() {
|
public WebvttParser() {
|
||||||
|
this.cueParser = new WebvttCueParser();
|
||||||
positionHolder = new PositionHolder();
|
positionHolder = new PositionHolder();
|
||||||
textBuilder = new StringBuilder();
|
textBuilder = new StringBuilder();
|
||||||
}
|
}
|
||||||
@ -130,11 +131,12 @@ public final class WebvttParser implements SubtitleParser {
|
|||||||
String line;
|
String line;
|
||||||
while ((line = webvttData.readLine()) != null && !line.isEmpty()) {
|
while ((line = webvttData.readLine()) != null && !line.isEmpty()) {
|
||||||
if (textBuilder.length() > 0) {
|
if (textBuilder.length() > 0) {
|
||||||
textBuilder.append("<br>");
|
textBuilder.append("\n");
|
||||||
}
|
}
|
||||||
textBuilder.append(line.trim());
|
textBuilder.append(line.trim());
|
||||||
}
|
}
|
||||||
CharSequence cueText = Html.fromHtml(textBuilder.toString());
|
|
||||||
|
CharSequence cueText = cueParser.parse(textBuilder.toString());
|
||||||
|
|
||||||
WebvttCue cue = new WebvttCue(cueStartTime, cueEndTime, cueText, cueTextAlignment, cueLine,
|
WebvttCue cue = new WebvttCue(cueStartTime, cueEndTime, cueText, cueTextAlignment, cueLine,
|
||||||
cueLineType, cueLineAnchor, cuePosition, cuePositionAnchor, cueWidth);
|
cueLineType, cueLineAnchor, cuePosition, cuePositionAnchor, cueWidth);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user